原子性:指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。(整体)
一致性:指事务前后数据的完整性必须保持一致。(完成)
隔离性:指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据 要相互隔离。(并发)
持久性:指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,即时数据库发生故障也不应该对其有任何影响。l (结果)
隔离问题:
脏读:一个事务读到另一个事务没有提交的数据
(B事务未提交,A事务此时读到了B事务未提交的数据,此时B数据回滚,A事务的数据是错的.)
不可重复读:一个事务读到另一个事务已提交的数据(update)
A事务读到的数据是B事务Update已经提交了的数据,我们执行A事务的语句读数据时B事务还未提交,然后再执行时(此 时B 事务 提交)A事务时,A事务读到的数据跟之前的不一致。
虚读(幻读):一个事务读到另一个事务已提交的数据(insert)
A事务读到的数据是B数据Insert已经提交了的数据,我们执行A事务的语句读数据时B事务还未提交,
然后再执行时(此时B 事务 提交)A事务时,A事务读到的数据跟之前的不一致。
具体可以看看 https://www.cnblogs.com/snsdzjlz320/p/5761387.html 例子
隔离级别:
DEFAULT--使用后端数据库默认的隔离级别(Spring中的选择项)
read_uncommitted:读未提交。存在3个问题
read_committed:读已提交。解决脏读,存在2个问题
repeatable_read:可重复读。解决:脏读、不可重复读,存在1个问题。
serializable:串行化。都解决,单事务。(可以理解为每个事务都上拿同一个锁)
MySQL默认采用REPEATABLE_READ隔离级别;Oracle默认采用READ_COMMITTED隔离级别
事务传播行为
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量(以两个事务的传播行为为例子):
- 1、TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
-
如果A没有事务,B将创建一个新的事务。
-
支持当前事务,A如果有事务,B将使用该事务。
-
- 2、TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。(必须新的)
-
如果A有事务,将A的事务挂起,B创建一个新的事务
如果A没有事务,B创建一个新的事务
-
- 3、TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
-
-
支持当前事务,A如果有事务,B将使用该事务。
如果A没有事务,B将以非事务执行。
- 4、TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
-
-
如果A有事务,将A的事务挂起,B将以非事务执行
如果A没有事务,B将以非事务执行
-
- 5、TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
-
-
如果A有事务,B将抛异常
如果A没有事务,B将以非事务执行
- 6、TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
-
-
支持当前事务,A如果有事务,B将使用该事务。
如果A没有事务,B将抛异常。
-
- 7、TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
- 拥有多个可以回滚的保存点,内部回滚不会对外部事务产生影响。只对DataSourceTransactionManager有效。
-
mysql 事务操作--Savepoint
需求:AB(必须),CD(可选)
Connection conn = null;
Savepoint savepoint = null; //保存点,记录操作的当前位置,之后可以回滚到指定的位置。(可以回滚一部分)
try{
//1 获得连接
conn = ...;
//2 开启事务
conn.setAutoCommit(false);
A
B
savepoint = conn.setSavepoint();
C
D
//3 提交事务
conn.commit();
} catche(){
if(savepoint != null){ //CD异常
// 回滚到CD之前
conn.rollback(savepoint);
// 提交AB
conn.commit();
} else{ //AB异常
// 回滚AB
conn.rollback();
}
}
事务超时
所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。
默认值-1,默认设置为底层事务系统(数据库底层)的超时值,如果底层数据库事务系统没有设置超时值,那么就是none,没有超时限制。
事务只读属性
“只读事务”并不是一个强制选项,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。
但是你非要在“只读事务”里面修改数据,也并非不可以,只不过对于数据一致性的保护不像“读写事务”那样保险而已。
因此,“只读事务”仅仅是一个性能优化的推荐配置而已,并非强制你要这样做不可
回滚规则
事务五边形的最后一个方面是一组规则,这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚(这一行为与EJB的回滚行为是一致的) 。但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。(下面基于注解或者xml文件 设置事务属性
rollbackFor | Class对象数组,必须继承自Throwable | 导致事务回滚的异常类数组 |
rollbackForClassName | 类名数组,必须继承自Throwable | 导致事务回滚的异常类名字数组 |
noRollbackFor | Class对象数组,必须继承自Throwable | 不会导致事务回滚的异常类数组 |
noRollbackForClassName | 类名数组,必须继承自Throwable | 不会导致事务回滚的异常类名字数组 |
- myBatis为例 基于注解的声明式事务管理配置@Transactional
- --例子转载自http://blog.csdn.net/bao19901210/article/details/41724355
spring.xml
添加tx名字空间
MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源与DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起作用。
@Transactional注解
@Transactional属性
属性 | 类型 | 描述 |
---|---|---|
value | String | 可选的限定描述符,指定使用的事务管理器 |
propagation | enum: Propagation | 可选的事务传播行为设置 |
isolation | enum: Isolation | 可选的事务隔离级别设置 |
readOnly | boolean | 读写或只读事务,默认读写 |
timeout | int (in seconds granularity) | 事务超时时间设置 |
rollbackFor | Class对象数组,必须继承自Throwable | 导致事务回滚的异常类数组 |
rollbackForClassName | 类名数组,必须继承自Throwable | 导致事务回滚的异常类名字数组 |
noRollbackFor | Class对象数组,必须继承自Throwable | 不会导致事务回滚的异常类数组 |
noRollbackForClassName | 类名数组,必须继承自Throwable | 不会导致事务回滚的异常类名字数组 |
@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。
- <!-- 事物切面配置 -->
- <tx:advice id="advice" transaction-manager="transactionManager">
- <tx:attributes>
- <tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
- <tx:method name="insert" propagation="REQUIRED" read-only="false"/> 这里可以设置事务属性
- </tx:attributes>
- </tx:advice>
- <aop:config>
- <aop:pointcut id="testService" expression="execution (* com.baobao.service.MyBatisService.*(..))"/>
- <aop:advisor advice-ref="advice" pointcut-ref="testService"/>
- </aop:config>
比较常用的是基于XML和注解
l 基于xml配置
1.配置事务管理器 jdbc:DataSourceTransactionManager hibernate:HibernateTransactionManager 2.事务通知(详情、属性) <tx:advice id="" transaction-manager=""> <tx:attributes> <tx:method name="add*"> <tx:method name="update*"> <tx:method name="delete*"> <tx:method name="find* read-only="true">
3. AOP编程,ABCD--> ABC <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com..*.*(..))"> |
l 基于注解
xml 配置 1.事务管理器
2.将配置事务管理器交予spring <tx:annotation-driven transaction-manager="....">
目标类 @Transactional 类 | 方法 |