我有一个使用JSF 2,Spring,JPA 2和 Hibernate Search 4.5.0 的项目。这个项目非常简单,但Hibernate Search给我带来了一些麻烦。
我使用Hibernate Search的唯一部分是索引Oracle View。这个视图,如果我运行一个简单的select count(*) from my_view
给了我 5756条记录,那就不多了。
嗯,我做的第一件事就是使用JPA / Hibernate Search进行实体映射:
@Entity
@Indexed
@Table(name = "my_view")
public class Person implements Serializable {
private static final long serialVersionUID = 244555315052436669L;
@Id
@Column(name = "id", insertable = false, updatable = false)
private Long id;
@Field(store = Store.NO, index = Index.YES, analyze = Analyze.YES)
@Column(name = "name", insertable = false, updatable = false)
private String name;
@Field(store = Store.NO, index = Index.YES, analyze = Analyze.YES)
@Column(name = "email", insertable = false, updatable = false)
private String email;
@Field(store = Store.NO, index = Index.YES, analyze = Analyze.YES)
@Column(name = "user", insertable = false, updatable = false)
private String user;
@Field(store = Store.YES, index = Index.YES, analyze = Analyze.YES)
@Column(name = "phone", insertable = false, updatable = false)
private String phone;
//Getters and Setters ommited
}
在映射我的实体之后,我构建了一个cronJob来索引这个实体。看看我用来索引我的实体的方法。
public void index() throws DAOException {
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(this.entityManager);
try {
fullTextEntityManager.createIndexer(Person.class)
.purgeAllOnStart(Boolean.TRUE)
.optimizeOnFinish(Boolean.TRUE)
.startAndWait();
}
catch (InterruptedException e) {
logger.error("Error creating index", e);
throw new DAOException(e);
}
}
并且,在为我的实体建立索引之后,我可以使用一个关键字搜索多个列。 问题是当我尝试搜索时没有任何关键字!我的应用程序必须返回所有记录。但我想,使用Hibernate Search,这种搜索速度非常快。我的错!
搜索需要超过40秒才能返回!在这里我如何搜索没有一个术语:
FullTextEntityManager fullTextEm = Search.getFullTextEntityManager(this.entityManager);
QueryBuilder qb = fullTextEm.getSearchFactory().buildQueryBuilder().forEntity(Person.class).get();
FullTextQuery fullTextQuery = fullTextEm.createFullTextQuery(qb.all().createQuery());
Sort sortField = new Sort(new SortField("name", SortField.STRING));
fullTextQuery.setSort(sortField);
return fullTextQuery.getResultList();
答案 0 :(得分:1)
查询执行可能非常快(因为没有关键字的搜索非常有效),但下一步的执行是将所有结果从数据库加载到内存中,并且从数据库加载5000多个元素永远不会要快,除非你研究实体和查询的缓存。
如果您需要加载所有内容,那么这不是全文查询,而是由标准关系查询更好地执行。 无论查询类型是什么 - 无论您使用投影 - 您可能还想调查模型导致的加载策略。通常,您可以通过调整某些关系的获取策略和/或启用二级缓存来获得显着提升。
答案 1 :(得分:0)
projections 解决了问题!感谢@chrylis,@RandomMooCow和@Sanne。
我不得不修改一些代码。我做的第一件事就是改变查询数据以使用投影的方式。
FullTextEntityManager fullTextEm = Search.getFullTextEntityManager(this.entityManager);
QueryBuilder qb = fullTextEm.getSearchFactory().buildQueryBuilder().forEntity(Person.class).get();
FullTextQuery fullTextQuery = fullTextEm.createFullTextQuery(qb.all().createQuery());
// I added this line to use projections
fullTextQuery.setProjection("id", "name", "email", "user", "phone");
Sort sortField = new Sort(new SortField("name", SortField.STRING));
fullTextQuery.setSort(sortField);
return fullTextQuery.getResultList();
但是,当我使用投影时,查询不会返回特定对象的列表(在我的情况下为Person.class
),而是列出Object[].class
。因此,我编写了一种将List<Object[]>
转换为List<Person>
的方法。这是代码:
private List<Person> toList(List<Object[]> objPeople) {
List<Person> lstPeople = new LinkedList<Person>();
Person Person = null;
for(Object[] objPerson : objPeople) {
Person p = new Person();
p.setId(Long.parseLong(objPerson[0].toString()));
p.setName(String.valueOf(objPerson[1] == null ? "" : objPerson[1]));
p.setEmail(String.valueOf(objPerson[2] == null ? "" : objPerson[2]));
p.setUser(String.valueOf(objPerson[3] == null ? "" : objPerson[3]));
p.setPhone(String.valueOf(objPerson[4] == null ? "" : objPerson[4]));
lstPeople.add(Person);
}
return lstPeople;
}
最后,我更改了我的实体以将值存储在索引上,因此Hibernate Search可以从索引中检索数据并转换为对象。我的实体:
@Entity
@Indexed
@Table(name = "my_view")
public class Person implements Serializable {
private static final long serialVersionUID = 244555315052436669L;
@Id
@Field(store = Store.YES, index = Index.YES, analyze = Analyze.YES)
@Column(name = "id", insertable = false, updatable = false)
private Long id;
@Field(store = Store.YES, index = Index.YES, analyze = Analyze.YES)
@Column(name = "name", insertable = false, updatable = false)
private String name;
@Field(store = Store.YES, index = Index.YES, analyze = Analyze.YES)
@Column(name = "email", insertable = false, updatable = false)
private String email;
@Field(store = Store.YES, index = Index.YES, analyze = Analyze.YES)
@Column(name = "user", insertable = false, updatable = false)
private String user;
@Field(store = Store.YES, index = Index.YES, analyze = Analyze.YES)
@Column(name = "phone", insertable = false, updatable = false)
private String phone;
//Getters and Setters ommited
}
它有效!