四大特性:ACID
原子性:一个事务必须视为一个不可分割的最小工作单元,一个事务要么全部完成,要么全部失败回滚,不可能只执行其中的一部分操作
- 一致性:事务执行的结果必须使数据库从一个一致性状态转换到另一个一致性状态,事务必须使数据库始终保持一致性状态
- 隔离性isolation:并发执行的事务之间彼此无法看到对方的中间状态
- 持久性:事务完成后所做的改动会持久化到数据库中
如果不考虑隔离性的话,高并发事务可能会出现的问题:
- 脏读 :事务A读取到事务B未提交成功的数据
- 不可重复读 :事务A读取到事务B提交的update后的数据,导致前后查询不一致
- 幻读:事务A读取到事务B提交的insert数据,导致前后数据查询不一致
五大隔离级别(√可能发生×不可能发生)
其中一个为default默认采用数据库默认隔离级别
mysql默认是Reptable Read可重复读
sqlServer,Oracle是Read Committed读已提交
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read Uncommitted(读未提交) | √ | √ | √ |
Read Committed(读已提交) | × | √ | √ |
Reptable Read(可重复读) | × | × | √ |
Serilizable (串行) | × | × | × |
七大传播机制
Propagation:事务传播机制,该属性的可选值如下
- required:支持当前事务,如果有就加入当前事务,如果当前方法没有事务,就新建一个事务,默认事务传播机制
- supports:支持当前事务,如果有就加入当前事务,如果当前方法没有事务,就以非事务的方式执行
- manatory:支持当前事务,如果有就加入当前事务,如果当前方法没有事务,就抛出异常,与never相反
- requires_new:新建事务,如果当前存在事务,就把当前事务挂起,如果当前方法没有事务,就新建事务
- not supported:以非事务方式执行,如果当前方法存在事务就挂起当前事务,如果当前方法不存在事务,就以非事务方式执行
- never:总是以非事务方法执行,如果当前方法存在事务就抛出异常,与manatory相反
- nested:如果当前方法有事务,则在嵌套事务内执行;如果当前方法没有事务,则与required操作类似,也就是新建一个事务
具体使用如下
1.xml配置
1.依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
2.spring配置文件
<!--配置事务管理器组件,注入事先定义好的数据源组件-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--为指定的事务管理器设置事务属性-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--定义属性,声明规则-->
<tx:attributes>
<tx:method name="find*" propagation="SUPPORTS"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--定义切面-->
<aop:config>
<!--定义切入点-->
<aop:pointcut id="serviceMethod" expression="execution(* cn.yinjian.curd.service..*.*(..))"/>
<!--将事务增强与切入点结合-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod"/>
</aop:config>
2.注解方式
@Transactional
spring配置文件
<!--配置事务管理器组件-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--添加对注解事务的支持-->
<tx:annotation-driven transaction-manager="transactionManager"/>
@Transactional
默认设置:
事务传播设置是required
事务隔离级别是default
事务是读/写
事务超时默认是依赖于事务系统的,或者不支持事务超时
任何RuntimeException将触发事务回滚,但是任何checked Exception不会触发事务回滚
面试题:spring 事物 关于在同一个类中一个方法调用另一个方法,事物的传播行为会失效
解决方法1:
将事务方法放到另一个类中(或者单独开启一层,取名“事务层”)进行调用,即符合了在对象之间调用的条件。
解决方法2:
获取本对象的代理对象,再进行调用。具体操作如:
-
Spring-content.xml上下文中,增加配置:<aop:aspectj-autoproxy expose-proxy=“true”/>
-
在xxxServiceImpl中,用(xxxService)(AopContext.currentProxy()),获取到xxxService的代理类,再调用事务方法,强行经过代理类,激活事务切面。
解决方法3:
很多时候,方法内调用又希望激活事务,是由于同一个方法既有DAO操作又有I/O等耗时操作,不想让耗时的I/O造成事务的太长耗时(比如新增商品同时需要写入库存)。此时,可以将I/O做成异步操作(如加入线程池),而加入线程池的操作即便加入事务也不会导致事务太长,问题可以迎刃而解。