事务有四大特性(ACID),原子性、持久性、一致性和隔离性
Java Spring框架提供了一种方便的方式来管理数据库事务,它支持多种事务隔离级别。事务隔离级别决定了事务在并发执行时的隔离程度,包括对其他事务的可见性和可能出现的并发问题。
以下是Spring框架支持的事务隔离级别及其详细说明:
ISOLATION_DEFAULT
(默认):这是系统的默认隔离级别。根据具体数据库来定义,大多数数据库默认级别是可重复读。ISOLATION_READ_UNCOMMITTED
(读未提交):在这个级别,一个事务可以看到其他未提交事务的变动。这种级别可以导致脏读、不可重复读和幻读的问题。ISOLATION_READ_COMMITTED
(读提交):在这个级别,一个事务只能看到其他事务已经提交的变动。这种级别可以避免脏读问题,但可能会出现不可重复读和幻读的问题。ISOLATION_REPEATABLE_READ
(可重复读):在这个级别,同一事务中多次读取的数据是一致的。这种级别可以避免脏读和不可重复读的问题,但可能会出现幻读的问题。ISOLATION_SERIALIZABLE
(可串行化):这是最高的事务隔离级别。在这个级别,事务串行化顺序执行,可以避免脏读、不可重复读和幻读的问题。但是这种隔离级别效率低下,因为事务通常需要等待前一个事务完成,才能继续执行。Spring事务的默认隔离级别与数据库一致
MySQL的事务隔离级别默认是可重复读(
REPEATABLE READ
),这是大多数数据库系统的默认设置。这个隔离级别可以避免脏读和不可重复读的问题,但可能会出现幻读的问题。
Java Spring框架中的事务传播行为是指在一个事务方法被另一个事务方法调用时,如何处理事务的传播。事务传播行为定义了在一个方法中调用另一个方法时,事务应该如何启动、提交或回滚。
以下是Spring框架支持的事务传播行为及其详细说明:
PROPAGATION_REQUIRED
(必需):如果当前存在一个事务,那么就加入这个事务,如果当前没有事务,就新建一个事务。这是最常见的选择。PROPAGATION_SUPPORTS
(支持):支持当前事务,如果当前没有事务,就以非事务方式执行。PROPAGATION_MANDATORY
(强制):使用当前的事务,如果当前没有事务,就抛出异常。PROPAGATION_REQUIRES_NEW
(新建):新建事务,如果当前存在事务,把当前事务挂起。PROPAGATION_NOT_SUPPORTED
(不支持):以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。PROPAGATION_NEVER
(从不):以非事务方式执行,如果当前存在事务,抛出异常。PROPAGATION_NESTED
(嵌套):如果当前存在一个事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。Spring的事务传播行为默认是
PROPAGATION_REQUIRED
,也就是如果当前存在一个事务,就加入该事务;如果当前没有事务,就新建一个事务。
在Spring中,事务有两种实现方式:
Spring 中手动操作事务和 MySQL操作事务类似,也是有 3 个重要操作
开启事务(获取事务)
提交事务
回滚事务
Spring Boot 内置了两个对象,DataSourceTransactionManager (事务管理器)用来获取事务(开启事务)、提交或回滚事务的,而 TransactionDefinition 是事务的属性,在获取事务的时候需要将 TransactionDefinition 传递进去从而获得一个事务 TransactionStatus 对象
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring.xml"})//加载配置文件
public class UserTest {
@Autowired
private UserService userService;
@Autowired
private TransactionDefinition transactionDefinition;
@Autowired
private DataSourceTransactionManager transactionManager;
@Test
public void test() {
final TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
try {
final User user = new User() {{
this.setUsername("test" + System.currentTimeMillis());
this.setPassword("123456");
this.setCreateTime(new Date());
}};
userService.addUser(user);
System.out.println(user);
// User(id=13, username=test1694675733277, password=123456, salt=null, token=null, isEnabled=null, createTime=Thu Sep 14 15:15:33 CST 2023, modifiedTime=null)
transactionManager.commit(transaction);
}catch (Exception e){
e.printStackTrace();
transactionManager.rollback(transaction);
}
}
}
声明式事务的实现,只需要在方法上添加 @Transactional 注解就可以实现,无序手动开启事务和提交事务,进入方法时自动开启事务,方法执行完全会自动提交事务,如果中途发生了没有处理的异常会自动回滚事务
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Transactional(rollbackFor = Exception.class)
@Override
public void addUser(User user) {
userMapper.insertSelective(user);
}
}
参数 | 作用 |
---|---|
value | 当你配置多个事务管理器时,可以使用该属性指定选择用哪个事务管理器 |
transactionManager | 同上 |
propagation | 事务的传播行为,默认值为 Propagation.REQUIRED |
isolation | 事务的隔离级别,默认值为 Isolation.DEFAULT |
timeout | 事务的超时时间,默认值为-1,如果超过该时间限制但事务还没完成,则自动回滚事务 |
readOnly | 指定事务是否为只读事务,默认值为 false,为了忽略那些不需要事务的方法,比如读取数据可以设置 read-only 为 true |
rolibackFor | 用于指定能够触发事务回滚的异常类型,可以指定多个异常类型 |
rolibackForClassName | 同上 |
noRolibackFor | 抛出指定的异常类型,不回滚事务,也可以指定多个异常类型 |
noRollbackForClassName | 同上 |
@Transactional(rollbackFor = Exception.class)
try catch语句块
捕获,未抛出异常(最难被排查到问题且容易忽略)Propagation
属性值设置错误即Propagation.NOT_SUPPORTED
(一般不会设置此种传播机制)@Component
public class TestServiceImpl {
@Resource
TestMapper testMapper;
@Transactional
void insert() {
int re = testMapper.insert(new Test(10,20,30));
if (re > 0) {
throw new Exception("need intercept");
}
}
}
@Component
public class TestServiceImpl {
@Resource
TestMapper testMapper;
@Transactional
public void insert() {
int re = testMapper.insert(new Test(10,20,30));
if (re > 0) {
throw new Exception("need intercept");
}
}
@Transactional
public void update() {
testMapper.update(new Test(10,20,30));
}
public void handle(){
update();
insert();
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Resource
TestServiceImpl testServiceImpl;
@Test
public void testInvoke(){
testServiceImpl.handle();
}
}
try catch语句块
捕获,未抛出异常@Component
public class TestServiceImpl {
@Resource
TestMapper testMapper;
@Transactional
void insert() {
try{
int re = testMapper.insert(new Test(10,20,30));
if (re > 0) {
throw new Exception("need intercept");
}
}catch(Exception e){
// 打印 不抛出新异常
}
}
}