1.什么是事务?
一荣俱荣,一损俱损。很多复杂的操作我们可以把它看成一个整体,要么同时成功,要么同时失败。
事务的四个特征ACID:
- 原子性(Atomic):表示组成一个事务的多个数据库的操作的不可分割的单元,只有所有的操作成功才算成功,整个事务提交,其中任何一个操作失败了,那么都会导致整个所有操作失败,事务就会回滚。
- 一致性(Consistentcy):事务操作成功后,数据库所处的状态和业务规则保持一致。如果A账户给B账户汇100,A账户要减去100,B加100,两个账户的总额是不变的。
- 隔离性(islation):在多个用户对数据库的操作相同的数据的时候,并发时,不同的事务有自己的数据空间,事务与事务之间不受干扰(不是绝对的)。干扰程度受数据库或者操作事务的隔离级别来决定,隔离级别越高,干扰就会越低,数据的一致性越好,并发就越差。否则反之。
- 持久性(Druability):一旦事务提交成功,数据就被持久化到数据库,不可以回滚。
2.Spring对事务的传播特性的控制
- 使用注解来处理事务传播特性
第一步:配置事务的管理器:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--数据源配置-->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/spring10"></property>
<property name="username" value="root"/>
<property name="password" value="123"/>
</bean>
<bean id="orderDao" class="spring.dao.impl.OrderDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="detailDao" class="spring.dao.impl.DetailDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="orderService" class="spring.service.impl.OrderServiceImpl">
<property name="orderDao" ref="orderDao"/>
<property name="detailDao" ref="detailDao"/>
</bean>
<!--定义事务的管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--事务管理器的注解驱动-->
<tx:annotation-driven transaction-manager="txManager"/>
</beans>
第二步:通过@Transactional来标注要使用事务的类或接口或方法,多数情况下,标注在类上或方法上,在项目中我们通常都会把事务开启在业务service层。
注意:只有Service层开启事务的时候,默认情况下,发生运行时异常会回滚,非运行时异常不会回滚。(重要)
可以设置对指定的异常回滚
rollbackForClassName或rollbackFor设置对哪些异常回滚。
noRollbackForClassName或rollbackFor设置对哪些异常不回滚。
@Transactional的默认传播特性是reqired,自实际项目中百分之八十都是采用这种
3.事务的传播特性:
- @Transactional(propagation = Propagation.REQUIRED):默认的传播特性,业务方法需要在一个事务中运行,如果一个方法已经处在一个事务中那么就加入到这个事务中,否则就会创建一个事务。
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveOrderAndDetail(Order order, Detail detail){
int orderId = orderDao.saveOrder(order);
detail.setOrderId(orderId);
detailDao.saveDetail(detail);
// int a = 1/0;
// throw new Exception();
}
- @Transactional(propagation = Propagation.NEVER):指定的业务方法绝对不能在事务范围内运行,如果业务方法在某个事务中执行,就会抛异常。只有业务方法没有任何事务才能正常执行。
- @Transactional(propagation = Propagation.MANDATORY):该属性指定业务方法只嗯呢该在一个存在的事务中执行,业务方法不能自己发起自己的事务,如果业务方法不存在事务,容器就抛异常。它完全和NEVER相反。
- @Transactional(propagation = Propagation.SUPPORTS):如果业务方法中已经在某个事务中被调用,则方法就成为事务的一部分,如果外部业务方法没有开启事务,supports该方法也会在没有事务的环境中执行。
- @Transactional(propagation = Propagation.NOT_SUPPORTED):如果该业务方法在一个事务中被调用,那么当前的事务会被挂起,执行该业务方法,方法执行完毕唤醒被挂起的事务。如果业务方法不在一个事务中执行,该方法也不会开事务。
- 不管是否在有无事务的环境中执行,都不开启事务。
- @Transactional(propagation = Propagation.REQUIRES_NEW):不管是否存在事务,业务方法总会自己开启一个事务,如果在已有事务的环境中调用,已有事务会被挂起,新的事务会被创建,直到业务方法调用结束,已有事务才被唤醒。
- 注意:内外事务不受影响。
- @Transactional(propagation = Propagation.NESTED):如果业务方法在一个事务中执行,就在这个事务中嵌套,如果没有事务就会按照REQUIRED执行,开启单独的事务。这种事务有多个事务的保存点,内部事务的回滚对外部事务没有影响。
- 注意:外部事务回滚,nested事务也跟着回滚。