事务回顾
1、事务的特性:
①原子性:事务是不可分割的,要么都成功,要么都失败
②一致性:事务发生前和事务发生后,事件的总数状态不变。(例如:转账操作)
③隔离性:多个事务在执行时可能会收到彼此的影响,所以采用了不同的隔离级别来避免这种影响
④持久性:事务一旦提交,它对数据的改变就是永久性的,即使数据库发生故障也不应该有什么影响。
2、多个事务造成的影响
①脏读:一个事务读取到了另一个事务还没有提交的数据(脏数据),导致了查询数据不一致的效果
②不可重复读:在同一个事务中,读取到了已经提交事务的数据,导致数据查询结果不一致。
③幻读:一个事务读取到了另一个事务提交的insert数据,导致查询出原来没有的数据。
3、幻读和不可重复读的区别
幻读是指读到了其他已经提交事务的新增数据,不可重复读是指读到了已经提交事务的更改数据(更改或者删除)
4、解决不可重复读的幻读的策略
》防止不可重复读到更改数据,只需要对操作的数据添加行级锁,阻止操作重的数据发生变化;
》防止读到新增数据,往往需要添加表级锁(将整张表锁定),防止新增数据(orcale使用多版本数据的方式实现)
Spring事务管理
spring提供了两种事务管理方式,一种是编程式事务,另一种是基于AOP的声明式事务,可以让程序从事务代码中解放出来。
不仅如此,Spring为事务管理提供了一致的编程模板,在高层次建立了统一的事务抽象。也就是说,不管是选择Spring JDBC、Hibernate还是MyBatis,Spring都可以让用户用统一的编程模型进行事务管理。
1、事务管理的抽象接口
Spring事务管理高层抽象主要包括3个接口,如下图所示:
①PlatformTransactionManager(平台事务管理器)
事务管理器是真正用来管理事务的,比如说事务的提交、回滚。对于不同的持久化框架,Spring提供了不同的平台事务管理器,当然最长用的就是spring Jdbc的TransactionManager。
②TransactionDefinition(事务定义信息)
描述事务的隔离级别、超时时间、是否为只读事务和事务传播规则等控制事务具体行为的事务属性,这些属性可以通过xml配置或者注解描述提供,也可通过手工编程设置。
》Isolation(隔离级别)
- default: :使用底层数据库默认的隔离级别
- read_uncommited :允许读取还未提交的并发事务还没提交的数据
- read_commited :允许读取并发事务提交后的数据
- repetable_read: :对相同事务的读取保证是一致的。
- serializeable :事务串行进行,安全但是慢。
》propagation(传播行为)
主要解决业务层方法之间的相互调用问题,到底使用那个方法中的事务,事务是如何进行传递问题。
- propagation_required:必须有事务,如果没有,就新建一个
- propagation_supports:事务可有可没有,如果没有,则在非事务的环境中执行
- propagation_mandatory:使用当前的事务,如果当前没有事务,抛出异常
- propagation_required_new:不管有没有事务,都会新进一个事务,原先的会被挂起。
- propagation_not_supported:如果当前存在事务,就把当前事务挂起
- propagation_never:以非事务执行,如果当前存在事务,则抛出异常
- propagation_nested:如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则
- 与porpagation_required类似。
》read-only(只读事务)
只读事务不修改任何数据,也就是说不能有update、delete、insert
》timeout(事务超时)
事务在超时前能运行多久。超时时间后,事务回滚。
③TransactionStatus(事务运行状态)
代表了一个事务的具体运行状态。事务管理器可以通过该接口获取事务运行期的状态信息,也可以通过该接口间接地回滚事务。该接口继承与SavepointManager接口,该接口下有如下方法:
- createSavepoint():创建一个保存点对象。
- realseSavepoint():释放一个保存点。如果事务提交,所有保存点将会自动释放。
- rollbackSavepoint():将事务回滚到特定的保存点上,被回滚的保存点将会自动释放
这三个接口的关系
Spring在进行事务管理的时候,会首先查看事务定义的信息(比如,使用了什么隔离级别,什么传播行为),然后通过平台事务管理器真正的去管理事务,在进行事务的管理的过程中,将产生相应的状态保存在了TransactionStatus中。
Spring支持两种方式事务管理
1、编程式的事务管理
Spring提供了TransactionTemplate模板类来支持编程式事务,TransactionTemplate和那些持久化模板类一样是线程安全的,所以可以在多个业务类中共享TransactionTemplate实例来进行事务管理。
如下,已一个简单的转账案例作为说明,如果转账过程中抛出异常,转账操作将不会完成
<!--基于数据源的事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--声明事务模板类-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
/**
* 编程式的事务管理
* @return void
*/
public void transforAccount(final String out, final String in , final Double money){
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
accountDao.outMoney(out,money);
int i=1/0;
accountDao.inMoeny(in,money);
}
});
}
2、使用xml配置声明式事务管理
Spring是通过AOP实现的。对代码的侵入性最小,可以让事务管理代码完全从业务代码中移除,非常符合非侵入式轻量级容器的理念。Spring在基于schema的配置中添加了一个tx命令空间,在配置文件中以明确结构化的方式定义事务属性,配合切面工具,使得业务类方法事务的配置得到极大的简化
<!-- 事务增强 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 对那些方法进行增强 -->
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="create*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="select*" propagation="SUPPORTS" read-only="true" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- 使用切点表达式定义目标方法 -->
<aop:config>
<!--通过aop定义事务增强切面,并引入事务增强-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.*.service..*.*(..))" />
</aop:config>
@Service
public class AccountService {
@Autowired
private AccountDao accountDao;
public void updateAccount(final String out, final String in , final Double money){
accountDao.outMoney(out,money);
int i=1/0;
accountDao.inMoeny(in,money);
}
}
3、使用注解配置声明式事务(@Transaction)
Spring还提供了基于 注解的事务配置,只需要在同期总配置基于注解的事务增强驱动,即可启动基于注解的声明式事务。
<!--基于数据源的事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启注解事务,对标注@Transaction注解的Bean进行加工处理,织入事务管理切面-->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="false" order="1"/>
@Transactional
@Repository
public class AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void outMoney(String out,Double money){
String sql="update account set money=money - ? where name = ?";
jdbcTemplate.update(sql,money,out);
}
public void inMoeny(String in,Double money){
String sql="update account set money=money + ? where name = ?";
jdbcTemplate.update(sql,money,in);
}
}
①<tx:annotation-driven>注解驱动属性说明
transaction-manager:引用指定的事务管理器
proxy-target-class:为true,表示通过cglib创建子类的方式来代理业务类;为false,表示使用jdk动态代理的方式。
order:如果业务类除事务切面外,还需要织入其他的切面,可以通过该属性来空值目标连接点的织入顺序。
② @Transaction注解的作用位置:
可被应用于接口定义和接口方法、类定义和类的public方法上。但是注解是不能继承的,所以在接口中添加的注解是不会被业务实现类继承的,所以一般添加在具体的业务实现类上。
- 类级别的注解只对public的方法起作用。
Spring的事务管理是基于接口管理或动态字节码技术的。对于基于动态代理的AOP事务来说,代理的方法只能是public或public final修饰的,不能使用static修饰。对于基于CGLIB字节码动态代理的方案,由于final、static、private修饰符的方法子类都不能覆盖,所以无法实施AOP 增强。
- 方法上的注解会覆盖类上的注解
③ @Transaction注解的默认属性:
事务隔离级别:isolation_default
事务传播行为:propagation_required
读写事务属性:读/写事务
超时时间: 依赖于底层的事务系统的默认值
回滚设置: 任何运行期异常引发回滚,任何检查性异常不会引发回滚
总结:
- Spring默认的事务回滚规则为:“运行期异常回滚,检查性异常不会滚”。
- Spring的事务管理是Spring AOP技术的典型应用。事务管理作为切面织入目标业务方法的周围,使得业务方法完全从事务代码中解脱出来,代码的复杂度大大降低。
- Spring的事务配置相对来说比较简单,主要提供两方面的信息:第一,切点的信息,用于定位实施事务切面的业务类方法;其二,控制事务行为的事务属性,这些属性包括隔离级别、事务的传播行为等
- Spring的传播机制可以很好的应对事务方法嵌套调用的情况。
- 因为单例对象不存在线程安全问题,所以经过事务管理增强的单实例Bean可以很好的工作在多线程环境中
- Spring AOP事务增强有两个方案:一个是基于接口的动态代理,另一个是基于CGLIB董改生成子类的代理。注意有些特殊的方法不能被Spring AOP代理,所以无法享受事务增强
- 在使用Spring JDBC如何直接获取Connection,可能会造成连接泄露。应尽量使用DataSourceUtils获取数据连接