为什么Hibernate Search很慢?即使记录很少

时间:2014-03-24 20:12:59

标签: java hibernate jpa hibernate-search

我有一个使用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();

2 个答案:

答案 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
}

它有效!