Hibernate Search:在没有数据库事务的情况下执行搜索查询

Hibernate Search: Perform search query without database transaction

我在 Spring 引导项目中使用 Hibernate Search 5.11.5。对于搜索,我使用投影,所以理论上我不需要公开交易。但是如果我删除 @Transactional 注释并调用 Search.getFullTextEntityManager(em),我会得到异常

java.lang.IllegalStateException: No transactional EntityManager available
     at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:288)
     at com.sun.proxy.$Proxy142.unwrap(Unknown Source)
     at org.hibernate.search.jpa.Search.getSession(Search.java:55)
     at org.hibernate.search.jpa.Search.getFullTextEntityManager(Search.java:49)

因为我因此使用投影 - 有什么方法可以防止这种强制使用事务?

问题

有点复杂,但我会尽力解释...

是的,Hibernate Search 能够 运行 在没有事务的情况下搜索查询。即使您正在加载实体,但显然不推荐这样做。无论如何,如果您只是使用投影而不加载任何实体,那绝对是可能的。

但是,Hibernate Search 总是需要一个 Session,即使它没有加载任何实体。我们称其为内部架构的限制。所以你不需要交易,但你确实需要一个会话。

现在,Spring 注入的实体管理器有点特殊,因为它们是代理。注入 bean 中的 EntityManager 代理会自动创建一个真正的 EntityManager 并委托给它……但前提是您处于事务中。如果您不在交易中,您将看到您看到的错误。

所以并不是 Hibernate Search 直接 需要一个事务,而是它需要一个实际的 EntityManager(不仅仅是代理),并且(至少通过默认)Spring 将只允许在交易中使用。

解决方案... ?

我建议您重新考虑并使用事务,除非您有非常具体的原因不想这样做,例如有限的数据库连接池。

否则...

Spring 中可能内置了解决方案。 OpenEntityManagerInViewFilter 浮现在脑海中,尽管这是一个有争议的解决方案。也许还有其他人,但不幸的是,我对 Spring 有点生疏。

或者,您可以尝试自己打开 Session。只要你不加载任何东西并且你不打开事务,它应该在性能方面相当便宜(特别是它不会获取数据库连接)。我想这就是您想要的?

它会是这样的:

@PersistenceUnit
EntityManagerFactory entityManagerFactory;

public List<MyProjection> search(...) {
    try (Session session = entityManagerFactory.unwrap(SessionFactory.class)
            .openSession()) {
        // ... do your things ...
        return hits;
    }
} 

最终,在未来的某个时候,将有一个专门的 API 用于无会话(因此无交易)搜索查询:https://hibernate.atlassian.net/browse/HSEARCH-3519 但就目前而言,上面的解决方案是必须的。