1. Spring事务相关可见:
Spring中的编程式、声明式事务https://blog.csdn.net/weixin_44929475/article/details/142559556
2. 常见场景
Spring事务失效可能发生在多种场景下,以下是一些常见的导致Spring事务失效的原因:
-
未指定回滚异常:
@Transactional
注解默认只对运行时异常(RuntimeException
)回滚,如果没有指定回滚异常类型,抛出其他类型的异常可能不会导致事务回滚。 -
异常被捕获:如果在方法内部通过
try-catch
捕获了异常,Spring AOP无法捕获到异常信息,导致无法进行事务回滚。 -
方法内部直接调用:如果在一个
@Transactional
注解的方法内部直接调用了另一个@Transactional
注解的方法,内部方法的事务会被忽略,导致事务失效。 -
异步多线程:在异步执行的线程中,事务不会自动传播,需要手动管理事务的边界。
-
使用了错误的事务传播机制:事务的传播行为设置不正确可能会导致事务失效。
-
方法被private或者final修饰:Spring的事务代理通常是通过动态代理实现的,这些代理要求目标方法是公开可访问的(
public
)。私有方法(private
)或最终方法(final
)无法被代理,因此事务将无效。 -
当前类没有被Spring容器托管:如果当前实体类上面没有打上
@Service
或@Repository
等注解,Spring容器不会对其进行管理,事务注解将不会生效。 -
数据库不支持事务:某些数据库存储引擎(如MySQL的MyISAM)不支持事务,只有支持事务的存储引擎(如InnoDB)才能使用Spring事务管理。
-
未启用Spring事务管理功能:需要在配置中启用Spring的事务管理功能,例如通过
@EnableTransactionManagement
注解。 -
业务和Spring事务代码不在同一个线程中:由于Spring事务实现中使用了
ThreadLocal
,业务代码必须和Spring事务代码在同一个线程中,否则事务将不会生效。
3. 实例
3.1 环境
相关环境搭建参考Spring事务文章。
3.2 代码
3.2.1 新增事务实现接口
public interface ITransactionalService {
/**
* 事务失效场景1:未指定回滚异常
*/
void rollbackException() throws Exception;
/**
* 事务失效场景2:异常被捕获
*/
void tryCatchException();
/**
* 事务失效场景3:方法内部调用
*/
void methodInnerInvoke();
/**
* 事务失效场景4:异步多线程
*/
void asyncThread();
/**
* 事务失效场景5:错误的事务传播机制
*/
void errorPropagation();
/**
* 事务失效场景6:方法被private或者final修饰
*/
void decoratedMethod();
/**
* 事务失效场景7:当前类没有被Spring容器托管
*/
void springNotManaged();
/**
* 事务失效场景8:数据库不支持事务
*/
void databaseNotSupportTransaction();
/**
* 事务失效场景9:未启用Spring事务管理功能
*/
void notEnabledTransaction();
/**
* 事务失效场景10:业务和Spring事务代码不在同一个线程中
*/
void notSameThread();
}
3.2.2 实现类
package com.xiaokai.service.impl;
import com.xiaokai.service.IOrderService;
import com.xiaokai.service.ITransactionalService;
import com.xiaokai.service.IUserService;
import com.xiaokai.tran.entity.OrderEntity;
import com.xiaokai.tran.entity.UserEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* Author:yang
* Date:2024-09-27 14:39
* Description:事务服务实现类,用于测试事务相关功能
*/
@Service
@Slf4j
public class TransactionalService implements ITransactionalService {
@Autowired
private IOrderService orderService;
@Autowired
private IUserService userService;
// 默认回滚异常为RuntimeException
@Transactional
@Override
public void rollbackException() throws Exception {
// 第一个业务逻辑
UserEntity user = getUserEntity();
Integer countUser = userService.createUser(user);
log.info("用户创建成功,用户ID:{}", user.getId());
// 抛出非运行时异常,测试事务回滚、
if (countUser ==1){
throw new ClassNotFoundException("非运行时异常,测试事务回滚");
}
// 第二个业务逻辑
OrderEntity order = getOrderEntity(countUser, user);
Integer countOrder = orderService.createOrder(order);
log.info("订单创建成功");
}
// 捕获异常后,不能进行异常回滚
@Transactional
@Override
public void tryCatchException() {
// 第一个业务逻辑
UserEntity user = getUserEntity();
Integer countUser = userService.createUser(user);
log.info("用户创建成功,用户ID:{}", user.getId());
// 抛出异常,测试事务回滚
try {
int i = 1/0;
// 第二个业务逻辑
OrderEntity order = getOrderEntity(countUser, user);
Integer countOrder = orderService.createOrder(order);
log.info("订单创建成功");
}catch (Exception e){
log.error("捕获异常,测试事务回滚失败", e);
}
}
// 内部调用事务方法
@Transactional
@Override
public void methodInnerInvoke() {
// 业务逻辑
innerInvoke();
// 业务逻辑
}
@Transactional
public void innerInvoke() {
UserEntity user = getUserEntity();
userService.createUser(user);
// 制造异常
int i = 1/0;
OrderEntity order = getOrderEntity(user.getId(), user);
orderService.createOrder(order);
}
// 异步调用事务方法
@Async
@Override
public void asyncThread() {
innerInvoke();
}
// 错误的传播属性
@Transactional(propagation = Propagation.NEVER)
@Override
public void errorPropagation() {
UserEntity user = getUserEntity();
userService.createUser(user);
// 制造异常
int i = 1/0;
OrderEntity order = getOrderEntity(user.getId(), user);
orderService.createOrder(order);
}
@Transactional
@Override
public void decoratedMethod() {
notSupportTransaction();
}
private void notSupportTransaction() {
UserEntity user = getUserEntity();
userService.createUser(user);
// 制造异常
int i = 1/0;
OrderEntity order = getOrderEntity(user.getId(), user);
orderService.createOrder(order);
}
// 该事务方法在另一个类中没有交给Spring管理
@Transactional
@Override
public void springNotManaged() {
}
// 数据库不支持事务MyISAM
@Transactional
@Override
public void databaseNotSupportTransaction() {
}
// 事务未开启,没有使用@EnableTransactionManagement注解和@transactional注解
@Transactional
@Override
public void notEnabledTransaction() {
}
// 事务方法在不同线程中调用
@Transactional
@Override
public void notSameThread() {
new Thread(() -> {
innerInvoke();
}).start();
}
private static OrderEntity getOrderEntity(Integer countUser, UserEntity user) {
OrderEntity order = new OrderEntity().builder()
.userId(countUser)
.number(10)
.name(user.getName())
.build();
return order;
}
private static UserEntity getUserEntity() {
UserEntity user = new UserEntity().builder()
.name("test")
.iphone("12345678901")
.address("test address")
.money(1000)
.password("123456")
.build();
return user;
}
}
3.2.3 order和user相关服务添加查询总记录数的方法和sql语句
IOrderService.java
public interface IOrderService {
Integer createOrder(OrderEntity orderEntity);
Integer totalOrderCount();
}
IUserService.java
public interface IUserService {
Integer createUser(UserEntity userEntity);
Integer totalUserCount();
}
ServiceImpl.java
@Service
public class IUserServiceImpl implements IUserService {
@Autowired
private UserMapper usermapper;
@Override
public Integer createUser(UserEntity userEntity) {
Integer count = usermapper.insertUser(userEntity);
return count;
}
@Override
public Integer totalUserCount() {
return usermapper.totalUserCount();
}
}
@Service
public class IOrderServiceImpl implements IOrderService {
@Autowired
private OrderMapper orderMapper;
@Override
public Integer createOrder(OrderEntity orderEntity) {
int count = orderMapper.insertOrder(orderEntity);
return count;
}
@Override
public Integer totalOrderCount() {
return orderMapper.totalOrderCount();
}
}
与数据库相关的部分照猫画虎即可!!!
3.3 测试
3.3.1 执行测试前后打印信息
// 执行测试前后,打印用户和订单数量
@Before
public void test_beforeCount() {
Integer userCount = userService.totalUserCount();
Integer orderCount = orderService.totalOrderCount();
log.info("before--- userCount: {},orderCount: {}", userCount, orderCount);
}
@After
public void test_afterCount() {
Integer userCount = userService.totalUserCount();
Integer orderCount = orderService.totalOrderCount();
log.info("after--- userCount: {},orderCount: {}", userCount, orderCount);
}
3.3.2 测试方法
3.3.2.1 无指定异常,抛出非默认异常时事务失效
// 执行测试
@Test
public void test_rollback() {
log.info("测试未指定异常回滚,抛出非运行时异常");
try {
transactionalService.rollbackException();
} catch (Exception e) {
log.error("捕获异常", e);
}
}
结果:可以看到执行方法前后user表增加了,order没有,事务回滚失效。
before--- userCount: 14,orderCount: 12
测试未指定异常回滚,抛出非运行时异常
用户创建成功,用户ID:21
捕获异常
java.lang.ClassNotFoundException: 非运行时异常,测试事务回滚
after--- userCount: 15,orderCount: 12
3.3.2.2 捕获异常,回滚失败
@Test
public void test_tryCatch() {
log.info("测试捕获异");
transactionalService.tryCatchException();
}
结果:可以看到执行方法前后user表增加了,order没有,事务回滚失效。
DatebaseHikariPool - Starting...
DatebaseHikariPool - Start completed.
before--- userCount: 15,orderCount: 12
测试捕获异
用户创建成功,用户ID:22
捕获异常,测试事务回滚失败
java.lang.ArithmeticException: / by zero
after--- userCount: 16,orderCount: 12
其他失效场景可以添加方法测试!实践出真知
注:完整测试类代码如下
package com;
import com.xiaokai.TransactionApplication;
import com.xiaokai.service.IOrderService;
import com.xiaokai.service.ITransactionalService;
import com.xiaokai.service.IUserService;
import com.xiaokai.service.impl.IOrderServiceImpl;
import com.xiaokai.tran.AnnotationService;
import com.xiaokai.tran.entity.OrderEntity;
import com.xiaokai.tran.entity.UserEntity;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.support.TransactionTemplate;
/**
* Author:yang
* Date:2024-09-26 11:09
*/
@SpringBootTest(classes = {TransactionApplication.class})
@Slf4j
@RunWith(SpringRunner.class)
public class TransactionTest {
@Autowired
private IOrderService orderService;
@Autowired
private IUserService userService;
@Autowired
private AnnotationService annotationService;
@Autowired
private ITransactionalService transactionalService;
// 注入编程式事务管理bean
@Autowired
private TransactionTemplate transactionTemplate;
@Test
public void test_connection() {
log.info("testTransaction");
Integer count = orderService.createOrder(new OrderEntity().builder()
.userId(1)
.name("xiaokai")
.number(10)
.build());
log.info("createOrder count: {}", count);
}
@Test
public void test_key_id() {
log.info("test_key_id");
UserEntity user = new UserEntity().builder()
.address("beijing")
.name("xiaokai")
.password("123456")
.iphone("13812345678")
.money(1000)
.build();
Integer count = userService.createUser(user);
}
@Test
public void test_program() {
log.info("test_program_transaction");
transactionTemplate.execute(status -> {
try {
// 添加用户
UserEntity user = new UserEntity().builder()
.name("xiaokai")
.iphone("13812345678")
.money(1000)
.address("beijing")
.build();
Integer countUser = userService.createUser(user);
if (countUser != 1) {
log.error("createUser error count: {}", countUser);
}else {
log.info("createUser count: {},user: {}", countUser, user.toString());
}
// 创建异常
//int i = 1 / 0;
OrderEntity order = new OrderEntity().builder()
.userId(user.getId())
.name("xiaokai")
.number(10)
.build();
Integer countOrder = orderService.createOrder(order);
if (countOrder != 1) {
log.error("createOrder count: {}", countOrder);
}else {
log.info("createOrder count: {},order: {}", countOrder, order.toString());
}
} catch (Exception e) {
status.setRollbackOnly();
log.error("transaction rollback", e);
}
return 1;
});
log.info("test_program_transaction end");
}
@Test
public void test_statement() {
log.info("test_statement_transaction");
annotationService.statementTransaction();
log.info("test_statement_transaction end");
}
// 执行测试前后,打印用户和订单数量
@Before
public void test_beforeCount() {
Integer userCount = userService.totalUserCount();
Integer orderCount = orderService.totalOrderCount();
log.info("before--- userCount: {},orderCount: {}", userCount, orderCount);
}
@After
public void test_afterCount() {
Integer userCount = userService.totalUserCount();
Integer orderCount = orderService.totalOrderCount();
log.info("after--- userCount: {},orderCount: {}", userCount, orderCount);
}
// 执行测试
@Test
public void test_rollback() {
log.info("测试未指定异常回滚,抛出非运行时异常");
try {
transactionalService.rollbackException();
} catch (Exception e) {
log.error("捕获异常", e);
}
}
@Test
public void test_tryCatch() {
log.info("测试捕获异");
transactionalService.tryCatchException();
}
}