什么是事务?
访问并可能更新数据库中各种数据项的一个程序执行单元
事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。
三种错误现象
脏读:读了别人修改但未提交的数据;
不可重复读:读数据期间别人修改了数据,导致前后读到数据不同(行);
幻读:读数据期间别人增删了数据,导致读到的数据行不同(表)。
mysql的底层支持(IndoDB事务模型)
开启事务:start transaction / begin / autocommit=0
锁释放:事务commit
快照读:不需要等待X锁释放,直接读快照(历史数据)
当前读:加排他锁(X锁),其他事务只能等待,
加共享锁(S锁),其他事务可以加S,但X锁只能等待;
查操作需要显示使用,增删改自动添加X锁。
四大隔离级别(分级处理策略)
隔离:事务解决并发问题的特征;
并发:多个事务(用户)同一时间,访问操作同一数据。
isolation_default(默认值)mysql:可重复读,oracle:读已提交;
Oracle没有 repeatable_read 这个值,但采用多版本比对(快照)方法实现可重复读。
高并发场景使用Read Committed,低并发场景使用Repeatable Read。
并发问题实战:乐观锁是应用锁,隔离属性是物理锁。
接口TransactionDefinition,定义了7种事务传播机制
传播:事务解决嵌套问题的特征;
存在问题:大事务嵌套很多小事务,导致大事务缺失原子性;
融合:取消小事务,以大事务为准。
1、propagation_required
默认设置,支持当前事务;如果不存在,创建一个新的。(增删改)
2、_supports
支持当前事务;如果不存在事务,则以非事务方式执行。(查,显示声明)
3、_requires_new
创建一个新事务,如果存在当前事务,则挂起(暂停)当前事务。(日志记录)
4、_mandatory
支持当前事务;如果当前事务不存在,抛出异常。
5、_not_supported
不支持当前事务,存在事务挂起当前事务,始终以非事务方式执行。
6、_never
不支持当前事务;如果当前事务存在,抛出异常。
7、_nested
当前事务存在,则在嵌套事务中执行,不存在,创建一个新的。
实战:
@Transactional //增删改(用默认值)
@Transactional(propagation=Propagation.SUPPORTS,readOnly=true) //查操作需要指定只读
应用
编程式事务管理:使用底层源码可实现更细粒度的事务控制。
transactionTemplate.execute
声明式事务管理:基于Spring AOP实现,本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
业务方法上添加@Transactional注释,定义事务的传播机制为required(默认)
<bean id="userService" class="com.yusael.service.UserServiceImpl"> <property name="userDAO" ref="userDAO"/> </bean> <!--DataSourceTransactionManager--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> @Transactional public class UserServiceImpl implements UserService { private UserDAO userDAO; <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
标签事务配置:如果开发中要为每一个方法都显示添加@Transactional会比较麻烦,所以用标签配置替代注释可以一劳永逸。
<tx:advice id="advice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="modify*"/> //增删改用默认属性 <tx:method name="get*" propagation="SUPPORTS" read-only="true"/> //读操作不开启新事务,指定只读。 </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="pc" expression="execution(* com.mj.service.*.*(..))"/> <aop:advisor pointcut-ref="pc" advice-ref="advice" /> //组装切面 </aop:config>