我们的服务是@Statefull
。大多数数据操作都是原子的,但在一组函数中我们希望在一个事务中运行多个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配置如下:
<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>
结果与自动提交模式相同:在每次本机查询之后,数据库(查看来自第二个连接的内容)会立即更新。
以手动方式使用交易的想法导致相同的结果:
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();
}
答案 0 :(得分:4)
如果您不使用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命名键设置final属性。
你也可以使用:
hibernate.transaction.manager_lookup_class=org.hibernate.transaction.JBossTransactionManagerLookup
或根据您当前的Java EE Application Server的其他策略。
答案 1 :(得分:1)
在阅读了关于每个配置属性和/或注释的另外一小时的主题后,我可以找到一个适用于我的用例的工作解决方案。它可能不是最好或唯一的解决方案,但由于问题已收到一些书签和赞成,我想分享我到目前为止所拥有的:
首先,在托管模式下运行持久性单元时,无法按预期工作。 (<persistence-unit name="test" transaction-type="JTA">
- 如果没有给出值,则JTA是默认值。)
我决定在持久性xml中添加另一个持久性单元,它被配置为以非托管模式运行:<persistence-unit name="test2" transaction-type="RESOURCE_LOCAL">
。
(注意:关于多个持久性单元的警告只是因为eclipse无法处理。它根本没有功能影响)
非托管persitence-context需要数据库的本地配置,因为它不再是容器提供的:
<persistence-unit name="test2" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>test.AEntity</class>
<properties>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost/test"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.password" value="1234"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.archive.autodetection" value="true" />
<property name="hibernate.jdbc.batch_size" value="20" />
<property name="hibernate.connection.autocommit" value="false" />
</properties>
</persistence-unit>
现在,只要您使用unitName
注释检索EntityManager的托管实例,就可以添加@PersistenceContext
项目所需的更改。
但请注意,您只能将@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();
}
}
现在,在原始帖子中给出的示例中,您需要注入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();
}
}
}
我的测试到目前为止表明,混合使用本机查询和持久性调用会产生所需的结果:所有内容都被提交或者事务作为一个整体回滚。
目前,解决方案似乎有效。我将继续在主项目中验证它的功能,并检查是否还有其他副作用。
我需要验证的另一件事是它是否会保存到:
ps。:这是草案1 。我将继续改进答案并指出我将要找到的问题和/或缺点。
答案 2 :(得分:0)
您必须将<hibernate.connection.release_mode key="hibernate.connection.release_mode" value="after_transaction" />
添加到您的媒体资源中。重启后应该进行事务处理工作。