我的应用程序有这个模型,我有一个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执行update
,merge
或saveOrUpdate
时,索引似乎没有更新,搜索正在返回对象的旧值。对于正在更新索引的delete
或save
操作,情况并非如此。
我见过有些人遇到同样的问题(lucene index not getting sync when any update occurs in DB through hibernate,merge 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;
}
但它仍然没有用。我会说我忽略了一些重要的事情,但我找不到有用的信息,所以任何帮助都会受到高度赞赏。
答案 0 :(得分:1)
当你说“搜索返回对象的旧值”时,你的意思是Hibernate Search返回的实体的getter返回过时的数据(更新前的值),或者你的意思是鉴于您的更新,返回的对象不应再与您的搜索查询匹配? 我将假设它是第二种选择。
首先,您应该检查每当您更改Elements
和Owner
之间的关联时,您都会更新两个方面的关联;如果您只是更新Elements.owner
而不是Owner.elementsCollection
,您将体验到您所描述的内容。
如果您正确更新了关联,那么您可能遇到了诸如HSEARCH-2868,HSEARCH-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