休眠“更新”操作后,Lucene的索引未被更新

时间:2018-04-24 07:34:10

标签: java hibernate lucene hibernate-search

我的应用程序有这个模型,我有一个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();
        }
    }
}

这项服务运行正常,但我遇到了一个大问题:当hibernate执行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;
}

但它仍然没有用。我会说我忽略了一些重要的事情,但我找不到有用的信息,所以任何帮助都会受到高度赞赏。

2 个答案:

答案 0 :(得分:1)

当你说“搜索返回对象的旧值”时,你的意思是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)< / p>

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

答案 1 :(得分:1)

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

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

Session