事务的基础知识
将Spring中的事务之前,先介绍一点事务的基本知识,方便后续对Spring中事务管理的理解
什么是事务?为什么要使用事务?
事务就是用来控制对数据资源进行访问的一组操作,保证事务前后,数字资源的状态都是正确的,举个例子,A转账给B 300元,此时需要从A的账户里扣除300元,然后给B的账户添加300元,此时要是给A扣款成功了,但是!给B添加300的操作失败了,那么现在A的账户被扣掉了300元,而B账户却没有增加300元,那么这个操作显然是不正确的,所以此时必须使用事务,来确保,就算B收款失败,而这300元也应该退还给A
事务4大特性
特性 | 说明 |
---|---|
原子性 | 一个事物中的所有操作看成1个原子操作,同时成功,或同时失败 |
一致性 | 事务操作对象的前后状态应该一致 |
持久性 | 一旦事务提交操作,对数据做的操作无法更改 |
隔离性 | 规定各种事务之间的相互影响程度 |
隔离级别
隔离级别 | 说明 |
---|---|
ReadUncommitted(读未提交) | 隔离级别最低,会产生脏读,不可重复读,幻读不可避免 |
ReadCommitted(读已提交) | 可避免脏读 但不可重复读,幻读不可避免,数据库的默认级别 |
RepeatedRead(重复读) | |
Serializable(串行读取) | 隔离级别最高,可以避免脏读,不可重复读,幻读,但是执行效率太低,一般不考虑 |
脏读,幻读,不可重复读
脏读:事务A读取到了事务B中尚未提交的数据,但是加入B回滚了,那么A读取到的B中的数据就是脏数据
不可重复读:同一个事务在整个过程中,前后两次读取同一数据,前后不一致问题
幻读:同一个事务在多次查询过程中,针对的总记录数量不同,比如第一次读取大于30岁的员工有100,同一事务过程中第二次读,发现大于30岁的员工只有25个了,这前后记录总数不一致就导致了幻读
Spring事务管理
Spring事务的设计理念
事务管理的关注点 和 数据访问关注点 分离。
JDBC的局部事务控制是由JDBC的Connection来完成的,也就是,必须使用同一个Connection来进行数据操作,才能实现局部事务。在事务开始前获取一个Connection,然后将该Connection绑定到当前线程,之后的数据访问对象都使用该同一Connection进行操作
事务的传播行为
传播行为 | 说明 |
---|---|
Requried(常用) | 当前存在事务,就加入,不存在事务就创建一个事务 |
Support | 存在事务就加入,不存在就直接执行 |
RequiredNew(常用) | 不管当前是否存在事务,都创建一个新事务 |
Never | 永远不需要当前事务,当前有事务就抛出异常 |
Nested | 当前存在事务,则在当前存在事务的一个嵌套事务种执行 |
A方法调用了B方法和C方法,当B方法传播行为被设置成 Required的时候,B方法会直接加入到A方法的事务种,设置C的传播行为为RequiredNew,就是不管A是否有开启事务,B都重新开启一个新事务
Spring种对事务做了统一支持:
PlatformTransactionManager:核心类,所有事务处理的顶级父类,每种事务的实现方式都是该类的实现类
TransactionDefinition:可以用来指定事务属性,可以指定的属性有:
1.事务的隔离级别
2.事务的传播行为
3.事务的过期时间
4.是否为只读(设置为true会对查询语句进行优化提高效率,不能有insert,update,delete类型语句)
TransactionTemplate:Spring提供的针对于编程式事务管理的模板方法类
TransactionAttribute:针对AOP进行声明式事务管理的场合,继承自 TransactionDefinition,提供了一个rollbackOn(Throwable ex),可以通过声明的方式,指定业务出现某些错误的情况下回滚事务。
TransactionStatus: 定义事务处理中的事务状态,在编程式事务中使用
PlatformTransactionManager的子类DatasourceTransactionManager:
了解几个概念:
tansaction object,承载当前事务的必要信息,DatasourceTransactionManager会根据transaction object来处理当前事务
TransactionSynchronization,可以注册到事务处理中的回调接口,可以当作式事务的监听器
TransactionSynchronizationManager,管理TransactionSynchronization,Spring中会将具体的事务资源,比如Connection绑定到该类
编程式事务
使用PlatformTransactionManager不同的实现类,就可以直接用编程的方式来实现事务,使用TransactionTemplate类来对PlatformTransactionManager进行模板化封装,可以将事务操作中很多相同的步骤同一封装。
直接使用PlatformTransactionManager进行事务管理
PlatformTransactionManager transactionManager = new DataSourceTransactionManager();
public void testPlatformTransaction(){
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
definition.setTimeout(20);
//还可以设置其他属性
TransactionStatus status = transactionManager.getTransaction(definition);
try{
}catch (RuntimeException e){
transactionManager.rollback(status); //回滚
}
}
使用TransactionTemplate进行事务管理
TransactionTemplate template = new TransactionTemplate();
Object result = template.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
Object result = null;
//这里可以写事务处理逻辑...
return result;
}
});
声明式事务
声明式事务才是Spring事务管理的核心精髓,也是使用最多的,声明式事务管理于SpirngAOP进行结合,只需要少量的编码就可以完成事务管理相关工作,虽然事务处理的粒度没有编程式事务那么细,但是声明式事务完全不依赖于业务代码,对业务代码没有任何入侵
有XML声明式和注解声明式,这里只将注解声明式,只需要是用@Transactional,对所需要进行事务管理的方法或类进行注解,如果注解的对象级别,那么对象中的方法将会继承对象级别上的事务,也就是所有方法都会加上事务,建议不要这么做,因为这样会拖慢执行效率,只在有必要的方法上加上@Transactional才是更推荐的写法。@Transactional只是一个声明,具体的事务实现需要通过反射读取所有需要被@Transactional表明的方法,并根据这些信息,才能使事务声明有效
使用@Transactional的方法
@Transactional(propagation = Propagation.REQUIRED,readOnly = true,timeout = 20)
public void testTransactional(){
//直接写数据处理逻辑即可
}
根据反射获取注解,并加入事务处理的伪代码
public void getAllMarkedByTransactional() {
try {
Method method = new Demo1().getClass().getDeclaredMethod("testTransactional",null);
//判断是否为Transactional注解
boolean isMarked = method.isAnnotationPresent(Transactional.class);
if(!isMarked){
//没有被@Transaction注解,直接执行方法即可
}
Transactional info = method.getAnnotation(Transactional.class);
TransactionTemplate transactionTemplate = new TransactionTemplate();
//设置事务传播行为
transactionTemplate.setTransactionManager(transactionManager);
transactionTemplate.setPropagationBehavior(info.propagation().value());
transactionTemplate.setReadOnly(info.readOnly());
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
Object result = null;
//这里可以写事务处理逻辑...
return result;
}
});
} catch (NoSuchMethodException e) {
e.printStackTrace();
} finally {
}
}
其实上面的代码根本不用我们自己写,Spring已经提供好了