首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Spring上的嵌套事务

Spring上的嵌套事务
EN

Stack Overflow用户
提问于 2011-03-08 20:45:11
回答 3查看 11.4K关注 0票数 15

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

让我们考虑一下下面的类:

代码语言:javascript
运行
复制
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();
    }
}

实体,如果是以下类:

代码语言:javascript
运行
复制
@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:

代码语言:javascript
运行
复制
<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>

和configuration类(我可以将其合并到applicationContext.xml中)。

代码语言:javascript
运行
复制
@Configuration
@ImportResource("/META-INF/applicationContext.xml")
public class Config {

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

为了保证persistence.xml文件的完整性:

代码语言:javascript
运行
复制
<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>

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

代码语言:javascript
运行
复制
DEBUG o.h.transaction.JDBCTransaction  - begin
singleInsert
DEBUG o.h.transaction.JDBCTransaction  - commit
Single insert: 1

第二个操作产生以下输出:

代码语言:javascript
运行
复制
batchInsert
singleInsert
DEBUG o.h.transaction.JDBCTransaction  - begin
First insert: 2
singleInsert
Second insert: 3
DEBUG

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

第三个操作会失败,并且根本不会创建事务:

代码语言:javascript
运行
复制
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 bean中,Spring确保对同一个类上的方法的调用是代理的,这显然不会发生在这里。有没有办法改变这种行为?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-03-15 23:45:11

在使用面向方面编程的proxy模式时,这种行为是Spring的文档化行为。可以通过切换到aspectj模式来更改它,该模式在编译或运行时执行代码插装。

票数 9
EN

Stack Overflow用户

发布于 2012-07-10 06:25:34

这并不是@Transactional的一个具体问题。这是由于您的<tx:annotation-driven/>的配置造成的。

Spring使用两种不同的AOP机制: JDK动态代理或CGLIB。JDK动态代理是默认的,它通过在run-time中使用接口来工作。CGLIB通过在compile-time生成子类来工作。如果你指定了<tx:annotation-driven proxy-target-class="true"/>,Spring将使用CGLIB,并且你的第二个@Transactional将被触发。

您可以阅读有关主题here的更多信息。

票数 7
EN

Stack Overflow用户

发布于 2020-01-28 02:55:35

处理@Transactional注释的默认建议模式是proxy,它只允许通过代理拦截调用。同一类中的本地调用不能以这种方式被拦截。对于更高级的截取模式,可以考虑切换到aspectj模式并结合编译时或加载时编织。

取自Spring引用。https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#tx-propagation-nested

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5232624

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档