Hibernate Search不会索引/重新索引实体

时间:2014-11-02 17:21:14

标签: java spring hibernate hibernate-search dbunit

我试图在我的项目中使用Hibernate Search(现在使用junit + dbunit编写测试),但搜索查询并没有返回任何结果。我昨天研究了这个问题并得出结论,问题是Hibernate Search与dbunit @DatabaseSetup一起工作得不好(类似于这个未回答的问题中的类似问题:link)。我将提供更多细节,但首先是第一,我的实体类:

@Entity
@Indexed
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "userId")
    private Long id;
    (...)
    @Column(nullable = false, unique = true)
    @Field(index = Index.YES, analyze=Analyze.YES, store=Store.NO)
    private String email;
    (...)
    @Column(nullable = false, unique = true)
    @Field(index = Index.YES, analyze=Analyze.YES, store=Store.NO)
    private String username;
    (...)
}

我通过我的DAO将其保存到db:

@Repository
public class UserDAOImpl implements UserDAO {

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public long save(User toSave) {

        return (Long) this.sessionFactory.getCurrentSession().save(toSave);
    }
(...)
}

这是负责运行lucene查询的代码:

@Override
    public List<User> searchByEmail(String email) throws InterruptedException {

        return generateHibernateSearchQueryFor("email", email).list();
    }

    private org.hibernate.Query generateHibernateSearchQueryFor(String field, String searchParam) {

        FullTextSession fullTextSession = Search.getFullTextSession(sessionFactory.getCurrentSession());
        QueryBuilder queryBuilder = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(User.class).get();

        org.apache.lucene.search.Query lQuery = queryBuilder.keyword().onFields(field).matching(searchParam).createQuery();
        org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery(lQuery, User.class);

        return fullTextQuery;
    }

这就是spring config中的配置方式:

<bean id="hibernate4AnnotatedSessionFactory"
          class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan" value="me.ksiazka.model" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop>
                <prop key="hibernate.cache.use_second_level_cache">true</prop>
                <prop key="hibernate.cache.use_query_cache">true</prop>
                <prop key="hibernate.hbm2ddl.auto">create</prop>

                <prop key="hibernate.search.default.directory_provider">filesystem</prop>
                <prop key="hibernate.search.default.indexBase">src/searching_indexes</prop>
            </props>
        </property>
    </bean>

现在我是如何测试的。我使用dbunit配置了测试数据集并创建了如下测试方法:

    @Test
    @DatabaseSetup("classpath:/testsDataset.xml")
    public void searchByEmailTest() {

        User u1 = new User("Maciej", "Adamowicz", "k2", "mac@gmial.com", "MacAda");
        userDAO.save(u1);

        List<User> u = null;
        try {
            //It worked at first - as new user was saved with hibernate he got his index in hibernate search indexes folder and searching found him.
            u = searchService.searchByEmail("mac@gmail.com");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //I know there should be asserts, its just for simplification for need of moment.
        System.out.println(":: " + u.size());
        System.out.println(":: " + u.get(0).getName());
    }


    List<User> u2 = null;
    try {
        //abc@gmial.com is in db - setted up by @DatabaseSetup
        u2 = searchService.searchByEmail("abc@gmail.com");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    //This didnt work, rows putted into db by dbunit doesn't have indexes in my indexing folder.
    System.out.println(":: " + u2.size());
    System.out.println(":: " + u2.get(0).getName());
}

在查看Hibernate Search文档后,我发现fullTextSession.createIndexer().startAndWait(); 方法。我使用它但它仍然不适用于来自@DatabaseSetup的行。无论如何,它使用我在测试之前推出的行&#34;手工&#34;使用sql所以我认为这只是dbunit的问题,只是使用@Before编写设置:

@Before
    public void setupDatabase() {

        if(!doneBefore) {

            try {
                //It calls createIndexer().startAndWait() to make sure everything is indexed before test
                searchService.reindex();
            } catch (InterruptedException e) {    
                e.printStackTrace();
            }

            User u1 = new User("Maciej", "Adamowicz", "k2", "mac@gmial.com", "MacAda");
            userDAO.save(u1);

            doneBefore = true;
        }

    }

运行此测试:

    @Test
    public void searchByEmailTest() {

        List<User> u = null;
        try {
            u = searchService.searchByEmail("mac@gmail.com");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //Also asserts here, I know.
        System.out.println(":: " + u.size());
        System.out.println(":: " + u.get(0).getName());
    }

尽管hibernate保存了数据,但它并没有工作。我试图找到bug并将我的代码恢复到测试通过的eariel版本(带有@DatabaseSetup的版本,但仅限于使用我的dao保存的行),现在这个也没有通过。我很困惑并且没有想法为什么它不会索引新对象,更不用说为什么在调用大量索引器时它不会重新索引所有数据库。任何帮助将不胜感激。

编辑:

在得到可能的答案后,我做了一些测试。关于搜索有时导致行数增加或增加三倍的事实,我尝试.purgeAll()并将索引提供程序更改为RAM,以确保在开始测试时我的索引是干净的。它没有任何基本的改变。为了构建我的索引,我使用了前面提到的.startAndWait()。试图用手工建造它&#34;使用.index(),但在尝试使用fullTextSession时遇到了一些嵌套的事务问题。明确提交交易(或设置@Rollback(false) - 尝试过两者)也不起作用。 我在Hibernate Search文档中找到的所有内容 - link。 如果我在搜索之前用DAO保存了一些东西,那么索引和搜索工作就可以正常工作了,但是做同样的事情@Before然后搜索就不起作用了。

2 个答案:

答案 0 :(得分:3)

当我没记错的话,Hibernate Search会在你提交交易时更新索引。

这对于普通代码来说没有问题,但在测试中这种行为可能会导致问题,因为测试的常见模式是,当您开始测试时启动事务,并且在测试结束时您扮演角色交易回来了,但你永远不会提交它们。

要验证这是导致问题的原因,请创建一个测试,启动明确的新事务,修改某些内容,然后提交事务。然后在提交后检查你的hiberante搜索索引。

答案 1 :(得分:1)

如此Hibernate Search doesn't index/reindex entities中所述,您需要在保存数据后显式提交事务以便进行索引。索引发生在事务后同步(至少每个默认值)。

您可以尝试使用手动索引API或群发索引器。我不确定为什么这对你不起作用。我也不确定@DatabaseSetup是如何工作的并且挂钩到JUnit生命周期中。

关于三重结果。您可能正在使用基于文件系统的索引(默认情况下使用),该索引会创建基于Lucene索引的索引,该索引在测试运行之间不会被清除。使用RAM索引或确保清理基于文件的索引。

如果你共享你的Hibernate属性配置,它可能会有所帮助。