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
但就目前而言,上面的解决方案是必须的。
我在 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 但就目前而言,上面的解决方案是必须的。