如何防止在事务处于活动状态时引发InvalidDataAccessApiUsage异常

时间:2019-04-18 18:36:38

标签: spring hibernate jpa spring-transactions spring-orm

在使用Spring 4.3.18时,我一直在将项目升级到Hibernate 5.4.2.Final。除了我在一项集成测试中收到的例外情况外,大多数事情都成功了。执行删除操作时,将引发异常。删除是列表中要迭代的几个对象之一。前8个成功,但第9个我收到以下异常:

  

org.springframework.dao.InvalidDataAccessApiUsageException:执行更新/删除查询;嵌套的异常是javax.persistence.TransactionRequiredException:执行更新/删除查询

我已经遍历了代码,可以看到我处于活动事务中,至少Spring正在报告,但是EntityManager报告它未加入事务。

Active Trasnaction is debugger

我的EntityManager在Spring中的接线方式如下:

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" primary="true">
    <property name="dataSource" ref="dataSource"/>
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="false" />
            <property name="databasePlatform" value="org.hibernate.dialect.MySQL55Dialect" />
        </bean>
    </property>
    <property name="jpaPropertyMap">
        <props>
            <prop key="hibernate.transaction.factory_class">org.springframework.orm.hibernate5.HibernateTransactionManager</prop>
        </props>
    </property>
</bean>

persistence.xml文件如下:

<persistence-unit name="myPersistenceUnit" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL55Dialect"/>
        <property name="hibernate.dialect.storage_engine=" value="innodb" />
        <property name="hibernate.hbm2ddl.auto" value=""/>
        <property name="hibernate.use_sql_comments" value="true"/>
        <!-- Implicit Naming Strategy ensures that table & column names are genereated as expected -->
        <property name="hibernate.implicit_naming_strategy" value="common.hibernate.model.naming.ImplicitNamingStrategy"/>
    </properties>
</persistence-unit>

在应用程序启动期间没有错误,但是有一些来自Hibernate的INFO和DEBUG消息:


2019-04-18 10:23:07,770 [main sessionId: vaultId: userId: origReqUri:] INFO  org.hibernate.validator.internal.util.Version  - HV000001: Hibernate Validator 6.0.14.Final
2019-04-18 10:23:09,559 [main sessionId: vaultId: userId: origReqUri:] DEBUG org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator  - No JtaPlatform was specified, checking resolver
2019-04-18 10:23:09,560 [main sessionId: vaultId: userId: origReqUri:] DEBUG org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformResolverInitiator  - No JtaPlatformResolver was specified, using default [org.hibernate.engine.transaction.jta.platform.internal.           StandardJtaPlatformResolver]
2019-04-18 10:23:09,581 [main sessionId: vaultId: userId: origReqUri:] DEBUG org.hibernate.engine.transaction.jta.platform.internal.StandardJtaPlatformResolver  - Could not resolve JtaPlatform, using default [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2019-04-18 10:23:09,581 [main sessionId: vaultId: userId: origReqUri:] INFO  org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator  - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2019-04-18 10:23:09,803 [main sessionId: vaultId: userId: origReqUri:] INFO  org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean  - Initialized JPA EntityManagerFactory for persistence unit 'default'

我根本无法确定EntityManager为什么在Spring中有活动时显示没有事务的原因。我假设我可能缺少某种配置,但是我无法确定是什么。

任何帮助将不胜感激。 谢谢...


经过进一步调查,似乎这是由LogicalConnectionManagedImpl#afterCompletion()方法引起的,该方法又在AbstractLogicalConnectionImplementor#resetConnection()方法中调用了resetConnection()的超类实现。

    protected void afterCompletion() {
    resetConnection( initiallyAutoCommit );
    initiallyAutoCommit = false;

    afterTransaction();
}

在resetConnection()中,连接状态设置为NOT_ACTIVE:

    protected void resetConnection(boolean initiallyAutoCommit) {
    try {
        if ( initiallyAutoCommit ) {
            log.trace( "re-enabling auto-commit on JDBC Connection after completion of JDBC-based transaction" );
            getConnectionForTransactionManagement().setAutoCommit( true );
            status = TransactionStatus.NOT_ACTIVE;
        }
    }
    catch ( Exception e ) {
       log.debug("Could not re-enable auto-commit on JDBC Connection after completion of JDBC-based transaction : " + e);
    }
}

这将导致EntityManager报告它未加入事务,即使Spring正在管理一个活动事务。这会导致此时所做的任何更新语句都失败,因为Hibernate认为没有活动的事务。

在我的persistence.xml文件中可以提供一个标志:

hibernate.allow_update_outside_transaction = true

但这对我来说似乎很危险,因为JPA规范出于充分的理由禁止这种行为。

Hibernate的resetConnection()方法中的这一更改显然是导致问题的原因,但我无法确定为什么现在要调用它。 Spring仍然相信有活跃的交易。当我的SQL工作在afterCommit同步中执行时,由于事务仍处于活动状态,因此实际上应该没有问题。

除了使用persistence.xml中的属性之外,还有其他方法可以解决此问题吗?

0 个答案:

没有答案