我试图在我的项目中使用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然后搜索就不起作用了。
答案 0 :(得分:3)
当我没记错的话,Hibernate Search会在你提交交易时更新索引。
这对于普通代码来说没有问题,但在测试中这种行为可能会导致问题,因为测试的常见模式是,当您开始测试时启动事务,并且在测试结束时您扮演角色交易回来了,但你永远不会提交它们。
要验证这是导致问题的原因,请创建一个测试,启动明确的新事务,修改某些内容,然后提交事务。然后在提交后检查你的hiberante搜索索引。
答案 1 :(得分:1)
如此Hibernate Search doesn't index/reindex entities中所述,您需要在保存数据后显式提交事务以便进行索引。索引发生在事务后同步(至少每个默认值)。
您可以尝试使用手动索引API或群发索引器。我不确定为什么这对你不起作用。我也不确定@DatabaseSetup是如何工作的并且挂钩到JUnit生命周期中。
关于三重结果。您可能正在使用基于文件系统的索引(默认情况下使用),该索引会创建基于Lucene索引的索引,该索引在测试运行之间不会被清除。使用RAM索引或确保清理基于文件的索引。
如果你共享你的Hibernate属性配置,它可能会有所帮助。