我们有一个服务是@Stateful
。大多数数据操作都是原子的,但在一组特定的函数中,我们希望在一个事务中运行多个本地查询
。
我们为 EntityManager
注入(inject)了事务范围的持久化上下文。在创建“一堆”普通实体时,使用 em.persist()
一切正常。
但是当使用 native 查询时(某些表不由任何 @Entity
表示),Hibernate 不会在同一事务中运行它们,但基本上每个查询使用一个事务。
所以,我已经尝试使用手动 START TRANSACTION;
和 COMMIT;
条目 - 但这似乎会干扰事务,hibernate 在混合 native 查询和持久性调用时用于持久化实体。
@Stateful
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 是这样配置的:
<persistence-unit name="test">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>java:jboss/datasources/test</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<property name="hibernate.transaction.jta.platform"
value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" />
<property name="hibernate.archive.autodetection" value="true" />
<property name="hibernate.jdbc.batch_size" value="20" />
<property name="connection.autocommit" value="false"/>
</properties>
</persistence-unit>
结果与自动提交模式相同:在每次 native 查询后,数据库(从第二个连接查看内容)立即更新。
以手动方式使用事务的想法导致相同的结果:
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();
}
最佳答案
如果您不使用 container managed transactions那么您还需要添加交易政策:
@Stateful
@TransactionManagement(value=TransactionManagementType.CONTAINER)
@TransactionAttribute(value=REQUIRED)
我只在两种情况下看到过这种现象:
DataSource
以自动提交模式运行,因此每个语句都在单独的事务中执行EntityManager
未配置@Transactional
,但随后只能运行查询,因为任何 DML 操作最终都会抛出事务所需的异常。
让我们回顾一下您设置了以下 Hibernate 属性:
hibernate.current_session_context_class=JTA
transaction.factory_class=org.hibernate.transaction.JTATransactionFactory
jta.UserTransaction=java:comp/UserTransaction
最终属性必须使用您的 Application Server UserTransaction JNDI 命名键进行设置。
您还可以使用:
hibernate.transaction.manager_lookup_class=org.hibernate.transaction.JBossTransactionManagerLookup
或根据您当前的 Java EE 应用程序服务器采取的其他一些策略。
关于java - 如何在同一个 Hibernate 事务中运行原生 SQL 查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25746509/