如何在同一个Hibernate事务中运行本机SQL查询?

我们有一个@Statefull服务。 大多数数据操作都是primefaces的,但在一组函数中我们希望在一个事务中运行多个native queries

我们为EntityManager注入了一个事务范围的持久化上下文。 在创建正常实体的“束”时,使用em.persist()一切正常。

但是当使用本机查询(某些表未由任何@Entity表示)时,Hibernate不会在同一事务中运行它们,但基本上每个查询使用一个事务。

所以,我已经尝试过使用手动START TRANSACTION;COMMIT; 条目 – 但这似乎干扰了事务,hibernate在混合本机查询和持久性调用时用于持久化实体。

 @Statefull class Service{ @PersistenceContext(unitName = "service") private EntityManager em; public void doSth(){ this.em.createNativeQuery("blabla").executeUpdate(); this.em.persist(SomeEntity); this.em.createNativeQuery("blablubb").executeUpdate(); } } 

此方法中的所有内容都应在一个事务中发生。 这可能与Hibernate一起使用吗? 在调试它时,可以清楚地看到每个语句都发生在任何事务的“独立”状态。 (即每次声明后立即将更改刷新到数据库。)


我已经使用最小设置测试了下面给出的示例,以便消除问题上的任何其他因素(字符串仅用于在每次查询后检查数据库的断点):

 @Stateful @TransactionManagement(value=TransactionManagementType.CONTAINER) @TransactionAttribute(value=TransactionAttributeType.REQUIRED) public class TestService { @PersistenceContext(name = "test") private EntityManager em; public void transactionalCreation(){ em.createNativeQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','c')").executeUpdate(); String x = "test"; em.createNativeQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','c','b')").executeUpdate(); String y = "test2"; em.createNativeQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('c','b','a')").executeUpdate(); } } 

Hibernate配置如下:

  org.hibernate.jpa.HibernatePersistenceProvider java:jboss/datasources/test         

结果与自动提交模式相同:在每次本机查询之后,数据库(查看来自第二个连接的内容)会立即更新。


以手动方式使用事务的想法导致相同的结果:

 public void transactionalCreation(){ Session s = em.unwrap(Session.class); Session s2 = s.getSessionFactory().openSession(); s2.setFlushMode(FlushMode.MANUAL); s2.getTransaction().begin(); s2.createSQLQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','c')").executeUpdate(); String x = "test"; s2.createSQLQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','c','b')").executeUpdate(); String y = "test2"; s2.createSQLQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('c','b','a')").executeUpdate(); s2.getTransaction().commit(); s2.close(); } 

如果您不使用容器管理的事务,那么您还需要添加事务策略:

 @Stateful @TransactionManagement(value=TransactionManagementType.CONTAINER) @TransactionAttribute(value=REQUIRED) 

我在两种情况下只看到过这种现象:

  • DataSource以自动提交模式运行,因此每个语句都在一个单独的事务中执行
  • EntityManager没有配置@Transactional ,但之后只能运行查询,因为任何DML操作最终都会抛出事务所需的exception。

让我们回顾一下,您已经设置了以下Hibernate属性:

 hibernate.current_session_context_class=JTA transaction.factory_class=org.hibernate.transaction.JTATransactionFactory jta.UserTransaction=java:comp/UserTransaction 

必须使用Application Server UserTransaction JNDI命名键设置final属性。

你也可以使用:

 hibernate.transaction.manager_lookup_class=org.hibernate.transaction.JBossTransactionManagerLookup 

或根据您当前的Java EE Application Server的其他策略。

在阅读了关于每个配置属性和/或注释的另一个小时的主题后,我可以找到一个适用于我的用例的工作解决方案。 它可能不是最佳或唯一的解决方案,但由于问题已收到一些书签和赞成,我想分享我到目前为止所拥有的:

首先,在托管模式下运行持久性单元时,无法按预期工作。 ( – 如果没有给出值,则JTA是默认值。)

我决定在持久性xml中添加另一个持久性单元,它被配置为以非托管模式运行:

(注意:关于多个持久性单元的警告只是因为eclipse无法处理。它根本没有function影响)

非托管persitence-context需要数据库的本地配置,因为它不再是容器提供的:

  org.hibernate.jpa.HibernatePersistenceProvider test.AEntity              

当您使用@PersistenceContext批注来检索EntityManager的托管实例时,现在需要对项目进行更改,即添加unitName

但请注意,您只能将@PersistenceContext用于托管持久性单元。 对于非托管的,您可以实现一个简单的Producer并在需要时使用CDI注入EntityManager:

 @ApplicationScoped public class Resources { private static EntityManagerFactory emf; static { emf = Persistence.createEntityManagerFactory("test2"); } @Produces public static EntityManager createEm(){ return emf.createEntityManager(); } } 

现在,在原始Post中给出的示例中,您需要注入EntityManager并手动处理事务。

 @Stateful public class TestService { @Inject private EntityManager em; public void transactionalCreation() throws Exception { em.getTransaction().begin(); try { em.createNativeQuery( "INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','a')") .executeUpdate(); em.createNativeQuery( "INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','b')") .executeUpdate(); em.createNativeQuery( "INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','c')") .executeUpdate(); em.createNativeQuery( "INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','d')") .executeUpdate(); AEntity a = new AEntity(); a.setName("TestEntity1"); em.persist(a); // force unique key violation, rollback should appear. // em.createNativeQuery( // "INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','d')") // .executeUpdate(); em.getTransaction().commit(); } catch (Exception e) { em.getTransaction().rollback(); } } } 

到目前为止,我的测试表明,混合使用本机查询和持久性调用会产生所需的结果:要么提交所有内容,要么将事务作为一个整体回滚。

目前,解决方案似乎有效。 我将继续validation它在主项目中的function,并检查是否还有其他副作用。

我需要validation的另一件事是它是否会保存到:

  • 将两个版本的EM注入一个Bean并混合使用。 (即使在同一张桌子上同时使用两个ems,第一次检查似乎也能正常工作)
  • 让两个版本的EM在同一数据源上运行。 (相同的数据源很可能没有问题,我认为相同的表可能会导致意外问题。)

ps。:这是草案1 。 我将继续改进答案并指出我将要找到的问题和/或缺点。

您必须将到您的属性中。 重启后应该进行事务处理工作。