如何在同一个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 。 我将继续改进答案并指出我将要找到的问题和/或缺点。
您必须将
到您的属性中。 重启后应该进行事务处理工作。
- 警告“违反协议的电话”
- java.lang.IllegalArgumentException“无法使用Spring 4和Hibernate 4设置DAO字段”
- 使用Hibernate JPA 2.1将应用程序部署到IBM WebSphere会产生NullPointerException
- 如何避免使用collection fetch指定的警告“firstResult / maxResults; 在内存中应用!“在使用Hibernate时?
- 如何在Spring / EJB / Mockito …代理上处理内部调用?
- 如何在spring mvc中使用ehcache和hibernate
- 如何在Hibernate中映射BigDecimal,以便恢复我放入的相同比例?
- 获取org.hibernate.MappingException:没有JDBC类型的Dialect映射:-4exception?
- Hibernate性能最佳实践?