【发布时间】:2014-12-29 09:29:07
【问题描述】:
我正在尝试在我的项目中使用 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 中的行。无论如何,它适用于我在使用 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());
}
虽然数据是由休眠保存的,但它不起作用。我试图找到错误并将我的代码恢复为测试通过的eariel版本(带有@DatabaseSetup但仅适用于使用我的dao保存的行),现在这个也没有通过。我很困惑,不知道为什么它不索引新对象,更不用说为什么在调用大量索引器时它不重新索引所有数据库。任何帮助将不胜感激。
编辑:
在可能的答案之后,我做了更多的测试。关于搜索有时会导致两倍或三倍行的事实,我尝试了.purgeAll() 并将索引提供程序更改为 RAM,以确保在开始测试时我的索引是干净的。基本没什么变化。如前所述,为了构建我的索引,我使用了.startAndWait()。尝试使用.index()“手动”构建它,但在尝试使用 fullTextSession 时遇到了一些嵌套事务问题。显式提交事务(或设置@Rollback(false) - 两者都尝试过)也不起作用。
我在 Hibernate Search 文档 - link 中找到的所有尝试。
如果我在搜索之前使用 DAO 保存某些内容,则索引和搜索工作正常,但在 @Before 执行相同操作然后搜索就不起作用。
【问题讨论】:
标签: java spring hibernate hibernate-search dbunit