Spring事务失效的原因
事务失效可能由多种原因引起,包括配置错误、事务传播行为不正确、异常处理不当等。接下来,我将结合实际场景详细解释这些原因,并提供代码示例来帮助理解。
Spring 事务失效的原因
1. 非事务方法调用事务方法
当一个非事务方法调用一个事务方法时,Spring 的事务代理机制不会生效。Spring 的事务是基于代理实现的,只有通过代理调用事务方法时,事务管理器才会被触发。
实际场景
假设我们有一个 OrderService,其中 createOrder 方法是事务性的,而 internalProcessOrder 方法不是事务性的。internalProcessOrder 方法调用了 createOrder 方法。
代码示例
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderService {
@Transactional
public void createOrder() {
// 模拟数据库操作
System.out.println("Creating order...");
// 可能会抛出异常导致事务回滚
throw new RuntimeException("Something went wrong!");
}
public void internalProcessOrder() {
// 非事务方法内部调用事务方法
createOrder();
}
}
问题
在 internalProcessOrder 方法中直接调用 createOrder 方法时,事务代理不会生效。即使 createOrder 是事务性的,但通过这种方式调用它,事务管理器不会被触发。
解决方法
确保事务方法的调用发生在代理上。可以通过注入自己的代理实例来实现。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderService {
private final OrderService orderServiceProxy;
@Autowired
public OrderService(@Qualifier("orderService") OrderService orderServiceProxy) {
this.orderServiceProxy = orderServiceProxy;
}
@Transactional
public void createOrder() {
// 模拟数据库操作
System.out.println("Creating order...");
// 可能会抛出异常导致事务回滚
throw new RuntimeException("Something went wrong!");
}
public void internalProcessOrder() {
// 通过代理调用事务方法
orderServiceProxy.createOrder();
}
}
2. 事务传播行为不正确
事务传播行为定义了在事务上下文中调用另一个事务方法时,事务应该如何传播。如果事务传播行为设置不正确,事务可能不会按预期生效。
实际场景
假设 OrderService 的 createOrder 方法调用了 PaymentService 的 processPayment 方法,而 processPayment 的事务传播行为被设置为 PROPAGATION_SUPPORTS。
代码示例
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.annotation.Propagation;
@Service
public class OrderService {
private final PaymentService paymentService;
@Autowired
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
@Transactional
public void createOrder() {
System.out.println("Creating order...");
paymentService.processPayment(); // 调用另一个事务方法
}
}
@Service
public class PaymentService {
@Transactional(propagation = Propagation.SUPPORTS) // 这里使用了 SUPPORTS
public void processPayment() {
System.out.println("Processing payment...");
throw new RuntimeException("Payment failed");
}
}
问题
processPayment 方法的事务传播行为是 PROPAGATION_SUPPORTS,它表示如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。因此,如果 processPayment 抛出异常,createOrder 方法的事务可能不会回滚。
解决方法
选择合适的事务传播行为。例如,如果希望方法始终运行在事务上下文中,应使用 PROPAGATION_REQUIRED。
@Service
public class PaymentService {
@Transactional(propagation = Propagation.REQUIRED)
public void processPayment() {
System.out.println("Processing payment...");
throw new RuntimeException("Payment failed");
}
}
3. 异常捕获和处理不当
如果在事务方法中捕获了异常但没有重新抛出,事务管理器可能不会得知方法执行失败,从而不会触发事务回滚。
实际场景
假设我们有一个 OrderService,在 createOrder 方法中捕获了异常,但没有重新抛出。
代码示例
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderService {
@Transactional
public void createOrder() {
try {
// 模拟数据库操作
System.out.println("Creating order...");
throw new RuntimeException("Something went wrong!");
} catch (Exception e) {
// 捕获异常但没有重新抛出
System.out.println("Exception caught: " + e.getMessage());
}
// 方法继续执行,事务管理器认为方法执行成功
}
}
问题
在 createOrder 方法中捕获了异常但没有重新抛出,事务管理器不会收到异常通知,因此不会触发事务回滚。
解决方法
在事务方法中捕获异常后,如果需要回滚事务,应重新抛出异常,或者配置事务管理器以在特定异常下自动回滚。
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderService {
@Transactional
public void createOrder() {
try {
// 模拟数据库操作
System.out.println("Creating order...");
throw new RuntimeException("Something went wrong!");
} catch (Exception e) {
// 捕获异常并重新抛出
System.out.println("Exception caught: " + e.getMessage());
throw e;
}
}
}
4. 事务管理器配置错误
如果事务管理器没有正确配置,事务可能不会生效。例如,没有正确配置数据源或事务管理器。
实际场景
假设我们配置了一个事务管理器,但没有正确设置数据源。
代码示例
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
@Configuration
public class AppConfig {
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("password");
return dataSource;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan("com.example.model");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
问题
如果事务管理器没有正确配置,事务可能不会生效。例如,数据源配置错误或事务管理器未正确注入。
解决方法
确保事务管理器正确配置,包括数据源、事务管理器和实体管理器。
5. 数据库事务隔离级别问题
如果数据库的事务隔离级别设置不正确,可能导致事务行为不符合预期。例如,默认的事务隔离级别是 READ_COMMITTED,可能会导致一些并发问题。
实际场景
假设我们有两个服务:OrderService 和 StockService。OrderService 的 createOrder 方法调用 StockService 的 updateStock 方法,但 updateStock 方法的事务隔离级别设置为 READ_COMMITTED。
代码示例
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Isolation;
@Service
public class OrderService {
private final StockService stockService;
@Autowired
public OrderService(StockService stockService) {
this.stockService = stockService;
}
@Transactional
public void createOrder() {
System.out.println("Creating order...");
stockService.updateStock();
}
}
@Service
public class StockService {
@Transactional(isolation = Isolation.READ_COMMITTED)
public void updateStock() {
System.out.println("Updating stock...");
throw new RuntimeException("Stock update failed");
}
}
问题
updateStock 方法的事务隔离级别是 READ_COMMITTED,这可能导致并发问题,事务可能不会按预期生效。
解决方法
确保事务隔离级别符合业务需求。例如,将 updateStock 方法的隔离级别改为 SERIALIZABLE。
@Service
public class StockService {
@Transactional(isolation = Isolation.SERIALIZABLE)
public void updateStock() {
System.out.println("Updating stock...");
throw new RuntimeException("Stock update failed");
}
}
总结
Spring 事务失效的原因多种多样,包括但不限于以下几点:
- 非事务方法调用事务方法:确保通过代理调用事务方法。
- 事务传播行为不正确:选择合适的事务传播行为。
- 异常捕获和处理不当:在事务方法中捕获异常后,重新抛出异常。
- 事务管理器配置错误:确保事务管理器正确配置。
- 数据库事务隔离级别问题:选择合适的事务隔离级别。
通过理解这些原因并采取相应的解决措施,可以有效避免事务失效问题,确保事务按预期工作。

646

被折叠的 条评论
为什么被折叠?



