事务有四个特性:ACID
原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
一致性(Consistency)事务前后数据的完整性必须保持一致,如果出现错误将全部回滚
隔离性(Isolation)事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离
持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
并发访问问题
如果不考虑隔离性,事务存在3种并发访问问题
1.脏读:一个事务读到了另一个事务未提交的数据
2.不可重复读:一个事务读到了另一个事务已经提交(update)(修改)的数据。引发另一个事务,在事务中的多次查询结果不一致
3.虚读 /幻读:一个事务读到了另一个事务已经提交(insert)(增加,删除)的数据。导致另一个事务,在事务中多次查询的结果不一致
事务的传播行为
一个方法调用另外一个方法,规定方法与方法之间的事务关系就是传播行为
当项目中仅仅使用hibernate,而没有集成进spring的时候,在service层中调用其他的业务逻辑方法,为了保证事物必须要把当前的hibernate session传递到下一个方法中,采用ThreadLocal的方法,将session传递给下一个方法,其实都是一个目的。现在这个工作由spring来帮助我们完成,就可以让我们更加的专注于我们的业务逻辑。而不用去关心事务的问题
在spring中大多数情况下只用:PROPGATION_REQUIRED:当我调用service层的方法的时候开启一个事务,那么在调用这个service层里面的其他的方法的时候,如果当前方法产生了事务就用当前方法产生的事务,否则就创建一个新的事务
Spring事务的传播行为定义了外层方法调用带有@Transactional的当前方法时,事务如何进行传播的规则
(1)propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中(Spring默认)
(2)propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行
(3)propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常
(4)propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起
(5)propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
(6)propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常
(7)propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作
事务隔离级别
read uncommited:最低的事务隔离级别,它允许另外一个事务可以看到这个事务未提交的数据(会出现幻读、脏读、不可重复读)
read commited:保证一个事物提交后才能被另外一个事务读取(该隔离级别禁止其他事务读取到未提交事务的数据,还是会造成幻读、不可重复读),(sql server默认级别)
repeatable read:保证一个事务不能被另外一个事务读取未提交的数据之外还避免了不可重复读(该隔离可防止脏读,不可重复读(重点在修改),但会出现幻读),(MySql默认级别)
serializable(序列化):代价最高最可靠的隔离级别,事务被处理为顺序执行。除了防止脏读,不可重复读之外,还避免了幻读(重点在增加与删除)
编程式和声明式事务
编程式事务使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate(模板)
TransactionTemplate template = new TransactionTemplate(txManager)
声明式事务建立在AOP之上,其本质是对方法前后进行拦截,在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务
声明式事务最大的优点是不需要通过编程的方式管理事务,不需要在业务逻辑代码中掺杂事务管理的代码,只需@Transactional注解的方式(或基于tx和aop名字空间的xml配置文件),便可以将事务规则应用到业务逻辑中,这正是spring倡导的非侵入式的开发方式
缺点是它的最细粒度只能作用到方法级别
根据代理机制的不同,Spring事务的配置方式
1、添加tx名字空间
xmlns:tx="http://www.springframework.org/schema/tx"
2、开启事务的注解支持
<!-- 开启事务控制的注解支持 --> <tx:annotation-driven transaction-manager="transactionManager"/>
3、MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源与DataSourceTransactionManager引用的数据源一致即可。
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation"> <value>classpath:mybatis-config.xml</value> </property> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
4、使用@Transactional注解
@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有public方法将都具有该类型的事务属性,我们也可以在方法级别使用该注解来覆盖类级别的定义
Spring事物是基于类和接口的,在调用的时候不能在同一个类里面被调用,必须调用外面的类去做事物操作,只有在使用基于接口的代理时它才会生效
Spring的事务必须是可见的,即注解应该只能被应用到public方法上,这是由Spring AOP的本质决定的(本质是动态代理),如果你在protected、private或者默认可见性的方法上使用@Transactional注解,这将被忽略也不会抛出任何异常