了解嵌套Spring@Transactional的工作原理

了解嵌套Spring@Transactional的工作原理,spring,jpa,transactional,Spring,Jpa,Transactional,我正在将一个EJB应用程序移植到Spring,我面临一些问题。 应用程序使用eclipselink以独立模式运行(这就是我们选择spring的原因) 在这个应用程序中,我需要创建一个订单,首先需要为其创建一个客户、订单行,然后为该订单添加付款 问题是,我希望在一个事务中执行所有插入操作,这样,如果无法持久化支付,就不必持久化任何内容。我试图做到这一点,但它看起来像我跨越多个独立的交易,因为在失败的情况下,数据会被保存到th DB(例如:支付失败,无论如何都会创建客户) 以下是切入点: publi

我正在将一个EJB应用程序移植到Spring,我面临一些问题。 应用程序使用eclipselink以独立模式运行(这就是我们选择spring的原因)

在这个应用程序中,我需要创建一个订单,首先需要为其创建一个客户、订单行,然后为该订单添加付款

问题是,我希望在一个事务中执行所有插入操作,这样,如果无法持久化支付,就不必持久化任何内容。我试图做到这一点,但它看起来像我跨越多个独立的交易,因为在失败的情况下,数据会被保存到th DB(例如:支付失败,无论如何都会创建客户)

以下是切入点:

public static void main(String[] args) {
    AbstractApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "applicationContext.xml" });
    BeanFactory beanFactory = (BeanFactory) context;
    MyService service = beanFactory.getBean(MyService.class);
    service.getNewOrders(true);
}
下面是我正在解析的bean(使用
beanFactory.getBean
):

它们都由Dao支持:

@Repository
public class CustomerDao {

    @PersistenceContext
    protected EntityManager em;

    public void create(Customer customer) {
        this.em.persist(customer);
    }
}
以下是maven pom.xml中的几个依赖项:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>3.2.3.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>3.2.3.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>3.2.3.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>3.2.3.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>3.2.3.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>com.jolbox</groupId>
        <artifactId>bonecp</artifactId>
        <version>0.8.0-rc1</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.25</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>eclipselink</artifactId>
        <version>2.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>javax.persistence</artifactId>
        <version>2.0.3</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
        <version>2.3.2</version>
    </dependency>

org.springframework
弹簧芯
3.2.3.1发布
org.springframework
春豆
3.2.3.1发布
org.springframework
spring上下文
3.2.3.1发布
org.springframework
德克萨斯州春季
3.2.3.1发布
org.springframework
春季甲虫
3.2.3.1发布
com.jolbox
博内克
0.8.0-rc1
mysql
mysql连接器java
5.1.25
org.eclipse.persistence
日食
2.3.2
org.eclipse.persistence
javax.persistence
2.0.3
org.eclipse.persistence
org.eclipse.persistence.jpa.modelgen.processor
2.3.2
以下是persistence.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" 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">
    <persistence-unit name="default" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <shared-cache-mode>NONE</shared-cache-mode>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/db" />
            <property name="javax.persistence.jdbc.user" value="root" />
            <property name="javax.persistence.jdbc.password" value="" />
        </properties>
    </persistence-unit>
</persistence>

org.eclipse.persistence.jpa.PersistenceProvider
假的
没有一个
弹簧配置如下所示:

@Service
@Transactional
public class CustomerService {

    @Autowired
    private CustomerDao customerDao;

    public void create(Customer customer) {
        // some works on customer fields (checking values etc)
        this.customerDao.create(customer);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<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:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">

    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

    <bean id="myDataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/db" />
        <property name="username" value="root" />
        <property name="password" value="" />
    </bean>

    <bean id="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="myDataSource" />
        <property name="jpaPropertyMap">
            <map>
                <entry key="eclipselink.weaving" value="false" />
            </map>
        </property>
    </bean>

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

    <tx:annotation-driven transaction-manager="myTxManager" />

    <context:component-scan base-package="com.application" />

</beans>

编辑


添加了从main创建ApplicationContext的功能,以及从MyService.java中省略的方法。既然服务方法是私有的,您如何调用它?它应该是公开的。私有方法不能被Spring事务拦截器截获,也不能被CGLib动态代理覆盖

编辑:好的,这是常见的问题。您正在从main方法调用方法
getNewOrders()
,它是公共的和事务性的。Spring拦截器截获此方法调用。因为没有事务,并且该方法被标记为支持,所以Spring不会启动任何事务

然后该方法调用private
handleOrder()
方法。注意,不仅方法是私有的,这使得Spring无法截获方法调用,而且调用是从组件中的方法到同一组件中的方法。因此,即使该方法是公共的,Spring也无法拦截该方法调用。事务处理是基于代理的:

method --> transactional proxy --> Spring bean 1 --> transactional proxy --> Spring bean2
在这种情况下,因为您没有调用另一个组件的方法,所以没有代理拦截,也没有启动事务

method --> transactional proxy --> Spring bean 1 --> transactional proxy --> Spring bean2
                                      ^   |
                                      |___|
所以,你要做的是

  • 例如,创建另一个Springbean的OrderHandler)
  • 将handleOrder()方法移动到此Springbean,使其公开
  • 在MyService中注入OrderHandler
它会很好的工作


这在中有详细说明。

不支持嵌套事务。如果在活动事务中调用带有@Transactional注释的方法,则不会发生任何事件该方法本身由
@Transactional(propagation=propagation.SUPPORTS,readOnly=true)
调用,我从main调用该方法。我试着从私有转换到公共,但仍然得到了相同的结果。有很多代码,哪一部分是相关的?大多数被截断的代码都是对getter/setter的调用,以填充对象,除了
OrderService.create
,它本身会创建另一个
@Transactional(Propagation.REQUIRES_NEW)
。我认为问题在于我的方法
handleOrder
不处理独立提交的内部事务。例如,在调用
this.customerService.create
customer.getId()之后,返回一个ID,该ID应保持为空,直到
handleOrder
结束。向我们展示从主方法到私有方法的调用链,该调用链应该是事务性的,而不是事务性的,以及获取对此/这些对象的引用的方式从MyService中添加了main和省略的方法,现在一切都应该在这里了,没有额外的代码会影响事务。可能与配置有关?非常感谢,这是一个很好的陷阱!我真的不喜欢它在这种特殊情况下的工作方式(与EJB相比),但似乎没有太多的选择来实现这一点。
method --> transactional proxy --> Spring bean 1 --> transactional proxy --> Spring bean2
                                      ^   |
                                      |___|