gpt4 book ai didi

spring - 了解嵌套的 Spring @Transactional 是如何工作的

转载 作者:行者123 更新时间:2023-12-02 04:57:37 25 4
gpt4 key购买 nike

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

在此应用程序中,我需要创建一个订单,为此我首先需要创建一个客户和 OrderLines,然后为此订单添加一个付款。

问题是我想在单个事务中完成所有插入,这样如果支付失败,则无需保留任何内容。我试图实现这一点,但看起来我正在跨越多个独立的交易,因为在失败的情况下,数据会持久保存到数据库中(例如:支付失败,无论如何都会创建客户)。

这里是入口点:

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

@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 支持:

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

这是 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>

Spring 配置如下:

<?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 中省略的方法

最佳答案

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

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

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

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

在这种情况下,由于您没有调用另一个组件的方法,因此没有代理拦截,也没有启动事务。

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

那么,你要做的就是

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

它会正常工作。

这在 Spring documentation 中有详细解释。 .

关于spring - 了解嵌套的 Spring @Transactional 是如何工作的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17698310/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com