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事务也跟着回滚。