了解嵌套Spring @Transactional的工作方式 [英] Understanding how nested Spring @Transactional works

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

问题描述

我正在将EJB应用程序移植到Spring,并且遇到了一些问题. 该应用程序通过eclipselink独立运行(这就是我们选择spring的原因).

I'm porting an EJB application to Spring and I'm facing some issues. The application is running in standalone (that's why we choose spring) with eclipselink.

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

In this application I need to create an Order, for which I first need to create a Customer, the OrderLines and then add a Payment for this Order.

问题是我想在一次交易中完成所有插入操作,因此,如果无法继续支付,则必须继续执行.我试图实现这一目标,但似乎跨多个独立事务,因为如果发生故障,数据将持久保存到数据库中(例如:付款失败,无论如何都会创建客户).

The problem is that I want to do all the insertion in a single Transaction so that if the payment fails to be persisted nothing must be persisted. I tried to achieve this but it looks like I'm spanning multiple independent transaction because in case of failure data are persisted to th DB (ex: payment fails, customer is created anyway).

这是入口点:

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):

Here is the bean I'm resolving (using beanFactory.getBean) :

@Component
@Scope("prototype")
public class MyService {

    @Autowired
    private CustomerService customerService;

    @Autowired
    private OrderService orderService;

    @Autowired
    private PaymentService paymentService;

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public void getNewOrders(boolean formattedOutput) {
        try {
            List<RawData> rawData = // Acquire data from a remote web service (http rest based)

            for (RawData data : rawData) {
                try {
                    this.handleOrder(data);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    private Order handleOrder(RawData rawData) throws Exception {
        Customer customer = new Customer();
        // Fill customer with rawData
        this.customerService.create(customer);

        Order order = new Order();
        order.setCustomer(customer);
        // Fill order with rawData
        this.orderService.create(order);

        Payment payment = new Payment();
        payment.setOrder(order);
        // Fill payment with rawData
        this.paymentService.create(payment);

        return order;
    }
}

每个服务如下所示:

@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);
    }
}

哪个都得到了Dao的支持:

Which are all backed by a Dao :

@Repository
public class CustomerDao {

    @PersistenceContext
    protected EntityManager em;

    public void create(Customer customer) {
        this.em.persist(customer);
    }
}

以下是来自maven pom.xml的一些依赖项:

Here are a few dependencies from 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>

这是persistence.xml:

Here is the 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>

弹簧配置如下:

<?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>

编辑

从主体添加了ApplicationContext的创建,并从MyService.java中省略了方法

Added the creation of the ApplicationContext from the main, and an omitted method from MyService.java

推荐答案

由于服务方法是私有的,如何调用它?它应该是公开的.私有方法不能被Spring事务拦截器拦截,也不能被CGLib动态代理覆盖.

How can you call the service method since it's private? It should be public. Private methods can't be intercepted by the Spring transactional interceptor, and can't be overridden by the CGLib dynamic proxy.

好的,这是常见的问题.您正在从main方法中调用方法getNewOrders(),该方法是公共的和事务性的. Spring拦截器拦截此方法调用.由于没有事务,并且该方法被标记为SUPPORTS,因此Spring不会启动任何事务.

OK, it's the usual problem. You're calling, from the main method, the method getNewOrders(), which is public and transactional. The Spring interceptor intercepts this method call. Since there is no transaction and the method is marked as SUPPORTS, Spring doesn't start any transaction.

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

Then this method calls the private handleOrder() method. Note that not only the method is private, which makes it impossible for Spring to intercept the method call, but the call is from a method in a component to a method in the same component. So even is the method was public, Spring could not intercept this method call. The transactional handling is proxy-based:

method --> transactional proxy --> Spring bean 1 --> transactional proxy --> Spring bean2

在这种情况下,由于您没有调用其他组件的方法,因此不会发生代理拦截,也不会启动任何事务.

In this case, since you're not calling a method of another component, there is no proxy interception, and no transaction is started.

method --> transactional proxy --> Spring bean 1 --> transactional proxy --> Spring bean2
                                      ^   |
                                      |___|

所以,您要做的是

  • 例如,创建另一个Spring bean'OrderHandler)
  • 将handleOrder()方法移至该Spring bean,使其公开
  • 在MyService中注入OrderHandler

它将正常工作.

春季文档.

这篇关于了解嵌套Spring @Transactional的工作方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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