java - 如何在同一个 Hibernate 事务中运行原生 SQL 查询?

标签 java mysql hibernate jpa transactions

我们有一个服务是@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/

相关文章:

java - 表格在 Jasper Report Java bean 中不可见

java - 指定默认 a4j :commandButton when press Enter in JSF form

java - 对少数实体进行审计的更好方法

mysql - 数据库名称可以以数字开头吗?

java - 空指针异常

java - 基本的一对多映射出现错误

java - “找不到或加载主类”是什么意思?

MySQL根据第一列对第二列和第三列进行排序

java - 多个应用请求的条件同步

java - 插入现有行/选择非空字段