Spring 上的嵌套事务 [英] Nested transaction on Spring

查看:32
本文介绍了Spring 上的嵌套事务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用嵌套的 Spring 事务时发现了一些奇怪的行为:当在同一个类中,注释为 @Transactional 的方法调用另一个也注释为 @Transactional 的方法时,未使用第二个注释.

I found some strange behavior when using nested Spring transactions: when, in the same class, a method annotated as @Transactional calls another method also annotated as @Transactional the second annotation is not used.

让我们考虑以下类:

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        final Main main = context.getBean(Main.class);
        // First Op
        System.out.println("Single insert: " + main.singleInsert());
        // Second Op
        main.batchInsert();
        // Third Op
        main.noTransBatchInsert();
    }

    @PersistenceContext
    private EntityManager pm;

    @Transactional(propagation=Propagation.REQUIRED)
    public void batchInsert() {
        System.out.println("batchInsert");
        System.out.println("First insert: " + singleInsert());
        System.out.println("Second insert: " + singleInsert());
    }

    public void noTransBatchInsert() {
        System.out.println("noTransBatchInsert");
        System.out.println("First insert: " + singleInsert());
        System.out.println("Second insert: " + singleInsert());
    }

    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public int singleInsert() {
        System.out.println("singleInsert");
        Pojo p = new Pojo();
        pm.persist(p);
        return p.getId();
    }
}

实体如果是以下类:

@Entity
public class Pojo {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @Override
    public String toString() {
        return "Pojo: " + id;
    }

    public int getId() {
        return id;
    }
}

和字符串部分 applicationContext.xml:

and the String parts applicationContext.xml:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task"
    xsi:schemaLocation="
   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
   http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
   http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">

    <tx:annotation-driven />

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="MyPersistenceUnit" />
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
</beans>

和配置类(我可以将它合并到 applicationContext.xml 中).

and the configuration class (I could have merge this in applicationContext.xml).

@Configuration
@ImportResource("/META-INF/applicationContext.xml")
public class Config {

    @Bean
    public Main main() {
        return new Main();
    }
}

为了完整性,persistence.xml 文件:

For completeness the persistence.xml file:

<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">

    <persistence-unit name="MyPersistenceUnit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>

        <properties>
            <property name="hibernate.hbm2ddl.auto" value="create" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
            <property name="hibernate.connection.driver_class" value="org.h2.Driver" />
            <property name="hibernate.connection.url" value="jdbc:h2:mem:TestDSJPA2;DB_CLOSE_DELAY=-1;LOCK_MODE=0" />
            <!--<property name="hibernate.connection.url" value="jdbc:h2:mem:TestDSJPA2;DB_CLOSE_DELAY=-1;LOCK_MODE=0" />-->
            <property name="hibernate.connection.username" value="sa" />
            <property name="hibernate.connection.password" value="" />
            <property name="hibernate.connection.autocommit" value="false"/>

            <property name="hibernate.c3p0.min_size" value="5" />
            <property name="hibernate.c3p0.max_size" value="20" />
            <property name="hibernate.c3p0.timeout" value="300" />
            <property name="hibernate.c3p0.max_statements" value="50" />
            <property name="hibernate.c3p0.idle_test_period" value="3000" />
        </properties>

    </persistence-unit>
</persistence>

因此在主类中,第一个操作按预期执行,即在新事务中.输出(包括一些调试消息)是:

So in the main class, the first operation is performed as expected that is in a new transaction. The output (including some DEBUG messages) is:

DEBUG o.h.transaction.JDBCTransaction  - begin
singleInsert
DEBUG o.h.transaction.JDBCTransaction  - commit
Single insert: 1

第二个操作给出以下输出:

The second operation gives the following output:

batchInsert
singleInsert
DEBUG o.h.transaction.JDBCTransaction  - begin
First insert: 2
singleInsert
Second insert: 3
DEBUG

这不是我所期望的,因为在使用 @Transactional(propagation=Propagation.REQUIRES_NEW) 注释 singleInsert 时,我希望为每个调用创建一个新事务,这不是因为相同而发生的两个插入都使用顶级事务.

This is not what I expected since in annotating singleInsert with @Transactional(propagation=Propagation.REQUIRES_NEW) I would expect a new transaction to be created for every call which is not what's happening since the same top level transaction is used for both insertion.

第三次操作失败,并且根本没有创建任何事务:

The third operation fails as well as no transaction is created at all:

noTransBatchInsert
singleInsert
DEBUG o.h.e.def.AbstractSaveEventListener  - delaying identity-insert due to no transaction in progress
First insert: 0
singleInsert
DEBUG o.h.e.def.AbstractSaveEventListener  - delaying identity-insert due to no transaction in progress
Second insert: 0

@Configuration beans Spring 确保对同一类上的方法的调用被代理,这显然不会在这里发生.有没有办法改变这种行为?

In the @Configuration beans Spring ensures that calls to the method on the same class are proxified which is obviously not happening here. Is there a way do change this behavior?

推荐答案

此行为是 Spring 对 AOP 使用 proxy 模式时记录的行为.可以通过切换到 aspectj 模式来更改它,该模式在编译或运行时执行代码检测.

This behavior is the documented behavior of Spring when using the proxy mode for AOP. It can be changed by switching to the aspectj mode which perform code instrumentation either on compilation or at runtime.

这篇关于Spring 上的嵌套事务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆