【问题标题】:Index on Lucene not being updated after hibernate "update" operation休眠“更新”操作后未更新 Lucene 的索引
【发布时间】:2018-04-24 07:34:09
【问题描述】:

我的应用程序有这个模型,我有一个 Element 抽象类和几个扩展它的类。这个元素有一个ElementType 并且可以有一个Owner。 现在我需要一个 REST 服务,它在多个字段上实现文本搜索:

@Entity
@Table(name = "elements")
@Inheritance(strategy=InheritanceType.JOINED)
@XmlRootElement
@AnalyzerDef(
    name = "textanalyzer",
    tokenizer = @TokenizerDef(factory = KeywordTokenizerFactory.class),
    filters = {
        @TokenFilterDef(factory = LowerCaseFilterFactory.class),
        @TokenFilterDef(factory = ASCIIFoldingFilterFactory.class)})
public abstract class Elements implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "elementid")
    private Integer elementId;

    @Basic(optional = false)
    @Column(name = "code", unique = true)
    @Field(index=Index.YES, analyzer = @Analyzer(definition = "textanalyzer"))
    private String code;

    @Basic(optional = false)
    @Field(index=Index.YES)
    @Column(name = "enabled")
    private boolean enabled;

    @IndexedEmbedded
    @JoinColumn(name = "ownerid", referencedColumnName = "ownerid")
    @ManyToOne(optional = true)
    private Owners owner;

    @IndexedEmbedded
    @JoinColumn(name = "elementtypeid", referencedColumnName = "elementtypeid")
    @ManyToOne(optional = false)
    private ElementTypes elementType; 
    .
    .
    .
}

@Entity
@Table(name = "owners")
@XmlRootElement
public class Owners implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "ownerid")
    private Integer ownerId;

    @Basic(optional = false)
    @Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO, analyzer = @Analyzer(definition = "textanalyzer"))
    @Column(name = "name")
    private String name;

    @OneToMany(mappedBy = "owner")
    @ContainedIn
    private Collection<Elements> elementsCollection;
    .
    .
    .
}

@Entity
@Table(name = "elementtypes")
@XmlRootElement
public class ElementTypes implements Serializable {
    @OneToMany(mappedBy = "elementType")
    @ContainedIn
    private Collection<Elements> elementsCollection;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "elementtypeid") 
    private Integer elementTypeId;

    @Basic(optional = false)
    @Column(name = "code", unique = true)
    @Field(index = Index.YES)
    private String code;
    .
    .
    .
   }

扩展Element的类示例:

@Entity
@Indexed
@Table(name = "digitalinputs")
@XmlRootElement
@PrimaryKeyJoinColumn(name="elementid")
public class DigitalInputs extends Elements {

    @Basic(optional = false)
    @Column(name = "position")
    private int position;


    @Basic(optional = true)
    @Column(name = "state")
    private boolean state;
    .
    .
    .
}

服务代码(太长,这里显示简化):

@Path("/search")
public class SearchController {

    @Context
    ServletContext context;

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response getElementsSearch(SearchRequestObject sro) {

        Session session = SessionUtil.getSession();
        FullTextSession fullTextSession = (FullTextSession)context.getAttribute("fullTextSession"); //indexes are rebuild at the beginning of execution, and the FullTextSession reference is stored in application context     
        try {
            org.apache.lucene.search.Query query;
            QueryBuilder qb = fullTextSession.getSearchFactory()
                    .buildQueryBuilder().forEntity(Elements.class).get();
            if(sro.isDisabled() || sro.isEnabled() || sro.getType() != null) {
                BooleanQuery.Builder builder = new Builder();

                //form the query
                .
                .
                .

            }
            // wrap Lucene query in a javax.persistence.Query
            javax.persistence.Query jpaQuery =
            fullTextSession.createFullTextQuery(query, Elements.class); 

            // execute search
            List<Elements> result = jpaQuery.getResultList();

            return Response.status(Response.Status.OK).entity(response).build();
        }catch(Exception e) {
            throw e;
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e).build();
        }
    }
}

此服务运行良好,但我有一个大问题:当在休眠状态下执行 updatemergesaveOrUpdate 时,索引似乎没有更新,并且搜索返回的旧值物体。 deletesave 操作不会发生这种情况,它们正在更新索引。

我见过一些人遇到同样的问题(lucene index not getting sync when any update occurs in DB through hibernatemerge not updating lucene index),但我没有得到实际的解决方案。 @ContainedIn 标签似乎没问题,我尝试在调用更新操作时同时刷新 Session 和索引:

@Override
public T editEntity(T bean) {
    T t = null;
    try {       
        this.createSession();
        this.createTransaction();

        //this.getSession().merge(bean);
        this.getSession().update(bean);
        this.getSession().flush();
        FullTextSession fts = (FullTextSession)ApplicationUtil.getServletContext().getAttribute("fullTextSession");
        fts.flushToIndexes();
        this.getTransaction().commit();
    }catch(Exception e) {
        this.getTransaction().rollback();
        throw e;
    }finally {
        this.closeSession();
    }

    return bean;
}

但它仍然无法正常工作。我想说我忽略了一些重要的事情,但我找不到对我的案例有用的信息,因此我们将非常感谢任何帮助。

【问题讨论】:

    标签: java hibernate lucene hibernate-search


    【解决方案1】:

    问题不是索引没有更新,而是FullTextSession引用了hibernate的Session的过时实例,所以基本上是对FullTextSession和索引刷新的误解。

    解决方案是在每次调用时使用 Session 的更新实例更新 FullTextSession 对象,而不是将其存储在上下文中:

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response getElementsSearch(SearchRequestObject sro) {                
        Session session = SessionUtil.getSession();
        FullTextSession fullTextSession = Search.getFullTextSession(session);
    
            .
            .
            .
        }catch(Exception e) {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e).build();
        }finally {
            session.clear();
            session.close();
        }
    }
    

    【讨论】:

      【解决方案2】:

      当您说“搜索正在返回对象的旧值”时,您的意思是 Hibernate Search 返回的实体的 getter 返回过时的数据(更新之前的值),还是您的意思鉴于您的更新,返回的对象不应再与您的搜索查询匹配? 我假设这是第二种选择。

      首先,您应该检查,无论何时更改ElementsOwner 之间的关联,都会更新双方 双方的关联;如果您只更新Elements.owner 而不是Owner.elementsCollection,您将体验到您所描述的内容。

      如果您正确更新关联,那么您可能遇到了诸如HSEARCH-2868HSEARCH-2486 之类的错误,甚至是其他错误。 @ContainedIn 在最近的版本中工作得更好,因此您应该考虑升级到 Hibernate Search 5.9(需要 Hibernate ORM 5.2.3+),或者至少升级到 Hibernate Search 5.6(需要 Hibernate ORM 5.0 或 5.1)

      如果这些都不起作用...请显示一个您实际更新实体的代码示例(即您调用设置器的位置)。你的例子根本没有改变我所看到的实体,所以我想你编辑了一些部分。

      【讨论】:

      • 很抱歉没有一开始就清除它。例如,如果我将Element 索引字段enabled 更改为true,并按启用=true 进行搜索,则此元素在结果中,但显示旧值(启用=false)。所以我的情况是你提到的第一个选择。如果我通过 REST get 调用获得相同的对象,则此对象显示更新后的值
      • 看起来索引实际上正在更新,您可能有不同的问题。您可以使用 Luke 检查索引内容:这应该有助于了解您是否在索引与查询方面遇到问题
      猜你喜欢
      • 1970-01-01
      • 2021-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-10
      • 2013-10-19
      相关资源
      最近更新 更多