Spring中编程式事务的实现:
此方式包含了三个重要的操作:获取事务、提交事务、回滚事务。
以及依赖两个重要的对象:DataSourceTransactionManager、TransactionDefinition
使用编程式事务示例:
@RestController
public class UserController {
@Autowired
private UserService userService;
@Resource
private DataSourceTransactionManager transactionManager;
@Resource
private TransactionDefinition transactionDefinition;
//此方法中使用编程式事务
@RequestMapping("/add")
public int add(UserInfo userInfo){
//此处非空校验
if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername())
|| !StringUtils.hasLength(userInfo.getPassword()))return 0;
//开启事务(获取事务
TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
int result = userService.add(userInfo);
System.out.println("受影响的行数:"+result);
//提交事务/回滚事务
transactionManager.commit(status);//提交事务
//transactionManager.rollback(status);//回滚事务
return result;
}
}
声明式事务的实现:(依赖注解@Transactional)
关于注解@Transactional,在进入方法之前,自动开启事务,在方法执行完之后,自动提交事务,如果出现异常则自动回滚。
关于注解@Transactional的作用域:
@Transactional除了可以修饰方法之外,还可以修饰类,当它修饰类的时候,则表示当前类中所有的public方法,都回自动的开启和提交(回滚)事务。@Transactional没有放在类上面的时候,那么一定要放在public方法上,否则不能生效。
关于注解@Transactional参数设置:
value: 当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器
transactionManager: 当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器
propagation: 事务的传播行为,默认值为Propagation.REQUIRED
isolation: 事务的隔离级别,默认值为lsolation.DEFAULT
timeout: 事务的超时时间,默认值为-1,如果超过该时间限制但事务还没有完成,则自动回滚事务
readOnly: 指定事务是否为只读事务,默认值为false;为了忽略那些不需要事务的方法,比如读取数据。可以设置read-only为true
rollbackFor: 用于指定能够触发事务回滚的异常类型,可以指定多个异常类型
rollbackForClassName: 用于指定能够触发事务回滚的异常类型,可以指定多个异常类型
noRollbackFor: 抛出指定的异常类型,不回滚事务,也可以指定多个异常类型
noRollbackForClassName: 抛出指定的异常类型,不回滚事务,也可以指定多个异常类型
关于isolation:事务的隔离级别:(5种)
进入isolation源码可以看到5种隔离级别:
DEFAULT:默认事务隔离级别,使用连接的数据库的事务隔离级别
READ_UNCOMMITTED:读未提交
READ_COMMITTED:读已提交
REPEATABLE_READ: 可重复读
SERIALIZABLE:串行化
注意:
1、当Spring 中设置了事务隔离级别和连接的数据库(MySQL)事务隔离级别发生冲突的时候,那么以Spring 的为准。
2、Spring中的事务隔离级别机制的实现是依靠连接数据库支持事务隔离级别为基础。
使用声明式事务示例:
//声明式事务
@Transactional(isolation = Isolation.DEFAULT) //在进入方法之前,自动开启事务,在方法执行完之后,自动提交事务,如果出现异常则自动回滚
@RequestMapping("/add1")
public int add1(UserInfo userInfo){
//此处非空校验
if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername())
|| !StringUtils.hasLength(userInfo.getPassword()))return 0;
int result = userService.add(userInfo);
System.out.println("受影响的行数:"+result);
return result;
}
很明显,以上示例是能正常提交事务的,下面咱们手动构造一个算术异常,就可以通过查看数据库看到事务进行了回滚:代码如下(在以上基础上手动构造了一行算术异常)
但是这里会有一个注意事项:
当@Transactional + try/catch的时候,这个时候出现异常,事务是不会自动回滚的,因为异常被捕获了之后没有进行任何处理,所以在事务看来一切正常,照常提交事务。当然解决方案有两个:
解决方案1:暴力解决,将异常重新又抛出去,这个时候声明式事务感知到异常了,会进行自动回滚操作:
解决方案2:使用代码的方式手动回滚当前事务
Transactional是怎么实现的?(Transactional的工作原理)
是通过AOP实现的,实现AOP的动态代理来实现的,提供了两种动态代理,一种是采用JDK的动态代理,一种使使用CGLIB动态代理。它的执行细节就是在执行方法之前,存放在IOC容器里面的不是原对象了,而是代理对象。在执行的时候,先执行代理对象的开启事务,然后执行目标对象里面的方法,如果目标对象出现异常了,那么就会rolleck进行回滚了,如果没有问题就会commit提交。
事务有4 大特性:
原⼦性:⼀个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执⾏过程中发⽣错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执⾏过⼀样。
⼀致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏
持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
隔离性:数据库允许多个并发事务同时对其数据进⾏读写和修改的能⼒,隔离性可以防⽌多个事务并发执⾏时由于交叉执⾏⽽导致数据的不⼀致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串⾏化 (Serializable)。
上⾯ 4 个属性,可以简称为ACID:原⼦性(Atomicity,或称不可分割性) ⼀致性(Consistency) 隔离性(Isolation,⼜称独⽴性) 持久性(Durability)
那为什么要设置事务的隔离级别?
设置事务的隔离级别是⽤来保障多个并发事务执⾏更可控,更符合操作者预期的。
如何设置Spring 中设置事务隔离?
Spring 事务中的5种隔离级别:
1. Isolation.DEFAULT:以连接的数据库的事务隔离级别为主。
2. Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读。
3. Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重复读。
4. Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读(MySQL默认级别)。
5. Isolation.SERIALIZABLE:串⾏化,可以解决所有并发问题,但性能太低。