hibernate搜索+ spring3 + jpa

时间:2009-12-21 11:20:29

标签: java spring jpa hibernate-search

我正在尝试将hibernate搜索集成到我的项目中。我的模型已编制索引,但由于某些原因,我的搜索查询不会返回任何结果。我一直试图解决这个问题几个小时,但我做的一切似乎都没有用。

域对象:

@Entity
@Table(name = "roles")
@Indexed
public class Role implements GrantedAuthority {
private static final long serialVersionUID = 8227887773948216849L;

    @Id @GeneratedValue
    @DocumentId
    private Long ID;

    @Column(name = "authority", nullable = false)
    @Field(index = Index.TOKENIZED, store = Store.YES)
    private String authority;

    @ManyToMany
    @JoinTable(name = "user_roles", joinColumns = { @JoinColumn(name = "role_id") }, inverseJoinColumns = { @JoinColumn(name = "username") })
    @ContainedIn
    private List<User> users;

    ...

}

DAO:

public abstract class GenericPersistenceDao<T> implements IGenericDao<T> {

@PersistenceContext
private EntityManager entityManager;

...

    @Override
    public FullTextEntityManager getSearchManager() {
        return Search.getFullTextEntityManager(entityManager);
    }

}

服务:

@Service(value = "roleService")
public class RoleServiceImpl implements RoleService {

    @Autowired
    private RoleDao roleDAO;

    ...

    @Override
    @SuppressWarnings("unchecked")
    public List<Role> searchRoles(String keyword) throws ParseException {
        FullTextEntityManager manager = roleDAO.getSearchManager();
        TermQuery tquery = new TermQuery(new Term("authority", keyword));
        FullTextQuery query = manager.createFullTextQuery(tquery, Role.class);
        return query.getResultList();
    }

}

测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
@Transactional
public class TestRoleService extends Assert {

    @Autowired
    private RoleService roleService;

    @Test
    public void testSearchRoles() {
       roleService.saveRole(/* role with authority="test" */);
       List<Role> roles = roleService.searchRoles("test");
       assertEquals(1, roles.size()); // returns 0
    }

}

配置

<persistence-unit name="hibernatePersistence" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <properties>
            <property name="hibernate.search.default.directory_provider" value="org.hibernate.search.store.FSDirectoryProvider" />
            <property name="hibernate.search.default.indexBase" value="indexes" />
        </properties>
</persistence-unit>

<!-- Entity manager -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="hibernatePersistence" />
</bean>

<!-- Transaction manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<!-- Enable the configuration of transaction behavior based on annotations -->
<tx:annotation-driven transaction-manager="transactionManager" />

<context:component-scan base-package="org.myproject" />

数据库实际上填充了与该权限字段值匹配的角色。实体经理有效,因为我的所有常规CRUD测试都成功。意思是错误完全是与hibernate搜索(3.1.1.GA)相关的,但哪里出错?

3 个答案:

答案 0 :(得分:2)

从理论上说这一切都有效但可能存在一些问题:

  • 您最初索引现有对象吗?虽然Hibernate Search索引所有新的更改,但它不知道预先存在的对象,因此您需要最初索引它们(使用ftem #index())
  • 默认情况下,HSearch挂钩到Hibernate或JTA事务以在事务事件之前和之后侦听。也许你的Spring tx配置会绕过它,因此HSearch不会被触发,因此无法索引。最好的方法是使用真正的JTA事务管理器并避免这些外观。
  • 如果您正在谈论初始索引(使用index()),您也可以使用#flushToIndexes()来强制索引,即使未提交tx也是如此。
  • 最后但并非最不重要的是,您的最后一段代码可能会抛出OutOfMemoryException,因为您在索引它们之前加载了内存中的所有对象。查看Hibernate Search参考文档,了解如何正确地批量索引对象的加载。 Manning的Hibernate Search in Action(我是作者)也深入研究了这一切。

答案 1 :(得分:1)

最后管理让它工作..显然对象没有自动索引..或至少没有提交。我的实现现在看起来如下:

public List<Role> searchRoles(String keyword) {
        // Index domain object (works)
        EntityManager manager = factory.createEntityManager();
        FullTextEntityManager ftManager = Search.getFullTextEntityManager(manager);
        ftManager.getTransaction().begin();

        List<Role> roles = ftManager.createQuery("select e from " + Role.class.getName() + " e").getResultList();
        for (Role role : roles) {
            ftManager.index(role);
        }
        ftManager.getTransaction().commit();

        // Retrieve element from search (works)
        TermQuery tquery = new TermQuery(new Term("authority", keyword));
        FullTextQuery query = ftManager.createFullTextQuery(tquery, Role.class);
        return query.getResultList();
}

通过执行index和getTransactionCommit函数,索引正确存储在我的索引文件夹中。然而,这个实现非常不自然,因为我为文本搜索创建了另一个实体管理器。是否有一种“更清洁”的方法来使用@Transactional注释

来索引(和提交)记录

答案 2 :(得分:1)

最后通过附加以下属性解决了我的问题: hibernate.search.worker.batch_size = 1

我现在不仅可以正确查询,而且每当我持久保存域对象时索引也会自动更新。我现在唯一的问题是通过我的import.sql插入的数据不会自动编入索引。是否有某种“魔术”hibernate.search属性可用于此问题,还是应该手动索引它们?