一、事务

1. 事务定义
  是一组执行单元,这个执行单元的各个组成部分要么同时执行成功,要么同时执行失败。

2. 事务特性(ACID)

具有4个基本特性:原子性、一致性、隔离性、持久性

  • 原子性(Automicity):一个事务是一个不可再分割的工作单位,事务中的全部操作要么都做,要么都不做;

  • 一致性(Consistency):事务必须是使得数据库状态从一个一致性状态,转变到另外一个一致性状态。也就是说在事务前和事务后,被操作的目标资源状态一致。比如银行转账案例中,转账前和转账后总账不变;

  • 隔离性(Isolation):一个事务的执行不能被其他事务所影响,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,多个并发事务之间不能相互干扰;

  • 持久性(Durability):一个事务一旦提交,它对数据库中数据的改变会永久存储起来,其他操作不会对它产生影响。


二、Spring中的事务管理

Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。

1. 编程式事务管理
  将事务管理代码嵌到业务方法中来控制事务的提交和回滚
  缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

2. 声明式事务管理
  一般情况下比编程式事务好用,将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。将事务管理作为横切关注点,通过AOP方法模块化,Spring中通过Spring AOP框架支持声明式事务管理。


三、Spring声明式事务管理

1. 前言
- JavaEE 体系进行分层开发,事务处理位于业务层, Spring 提供了分层设计业务层的事务处理解决方案;
- Spring 框架为我们提供了一组事务控制的接口;
- Spring 的事务控制都是基于 AOP 的,它既可以使用编程的方式实现,也可以使用配置的方式实现。

2. 事务管理器
  Spring的核心事务管理抽象,管理封装了一组独立于技术的方法,无论使用Spring的哪种事务管理策略(编程式或者声明式)事务管理器都是必须的。

org.springframework.transaction.PlatformTransactionManager,此接口是 Spring 的事务管理器,它提供了常用的操作事务的方法

PlatformTransactionManager

PlaformTransactionManager接口,包含三个方法:

  • 获取事务状态信息:TransactionStatus getTransaction(TransactionDefinition definition)

  • 提交事务:void commit(TransactionStatus status)

  • 回滚事务:void rollback(TransactionStatis status)

在开发中通常使用其实现类,如:
- SpringJDBC 或 iBatis 进行持久化数据时使用org.springframework.jdbc.datasource.DataSourceTransactionManager
- Hibernate 版本进行持久化数据时使用org.springframework.orm.hibernate5.HibernateTransactionManager

3. 事务

JDBC事务

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

Hibernate事务

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

JPA事务

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

Java原生API事务

通常用于跨越多个事务管理源(多数据源),则需要使用下面的内容

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="transactionManagerName" value="java:/TransactionManager" />
</bean>

四、事务属性

事务属性

1. 事务隔离级别

并发事务会导致发生以下三种类型的问题:

问题 例如
脏读 一个事务读取了另一个事务改写尚未提交的数据时,改写的数据被回滚了,那么第一个事务获取的数据无效
不可重复读 当同一个事务执行两次及以上相同的查询时,每次都得到不同的数据。一般因为另一并发事务在两次查询期间进行了更新
幻读 第一个事务读取一些数据,此时第二个事务在该表中插入了一些新数据,这时第一个事务再读取相同的数据就会多几行

不可重复读和幻读的区别:不可重复读侧重点在相同数据被修改,而幻读是删除或新增

Spring中事务的隔离级别可以通过隔离属性指定

隔离属性
从理论上讲,事务应该完全隔离,避免并发事务导致的问题,但是这样可能对性能产生极大影响,因为事务必须按顺序进行了。所以在实际的开发中,为了提升性能,事务会以比较低的隔离级别运行。

2. 事务传播行为
  事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如方法可能继续在现有事务中运行,也可能开启一个新的事务,并在自己的事务运行,Spring中的事务传播行为可以由传播属性指定。

事务传播行为

  • REQUIRED:当前方法必须有事务环境,如果当前方法已经有事务环境就加入,如果当前方法没有事务环境就新建一个新的事务;适合增删改操作;

  • SUPPORTS:表示当前方法支持事务,有事务可以运行没有事务也可以;适合查询操作;

  • REQUERS_NEW:当前方法必须有事务环境;不管当前方法有没有事务,都新建一个新的事务。

当前事务挂起:意思是先执行刚创建的事务,刚创建事务执行完后,再执行刚挂起的事务。

3. 事务的超时时间
  事务可以在行和表上获得锁,因此长事务会占用资源,并对整体性能产生影响。可以配置超时事务属性,事务在强制回滚之前可以保持多久,这样可以避免长期运行的事务占用资源。
  默认值是-1,没有超时限制;如果要设置,以秒为单位。

4. 是否是只读事务
  如果事务只读数据但不修改可以通过配置只读事务属性,帮助数据库引擎优化事务。只读事务属性:表示这个事务只读读取数据,但是不更新数据建议查询时设置为只读。


五、XML方式实现事务

1. pom.xml添加依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
<!--aop支持-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.7</version>
</dependency>

2. ApplicationContext.xml配置事务

 <!--1.开启注解扫描-->
<context:component-scan base-package="com.zz"/>

<!--5.Spring声明式事务配置-->
<!--5.1 配置事务管理器(事务切面)-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!--5.2 配置事务通知规则(拦截到方法后如何管理事务)-->
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <!--find/select/get开头的方法,有事务可以无事务也可以,只读的事务-->
        <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
        <tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
        <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
        <!--* 表示其他所有的方法就是指增删改操作,必须有事务环境,读写事务-->
        <tx:method name="*" propagation="REQUIRED" read-only="false"/>
    </tx:attributes>
</tx:advice>
<!--5.3 Aop配置:切入点表达式 + 通知规则-->
<aop:config>
    <!--5.3.1 配置切入点表达式-->
    <aop:pointcut id="pt" expression="execution(* com.itheima.service.impl.*ServiceImpl.*(..))" />
    <!--5.3.2 建立切入点表达式与通知规则的关系-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>

六、注解方式实现事务

1. 修改ApplicationContext.xml

开启事务注解支持

2. service层使用注解

/**
 * @Transactional 事务注解支持
 * 1. 可以修饰在实现类上,表示当前实现类的所有方法都应用事务。
 * 2. 也可以修饰在方法上,表示当前方法应用事务。
 * 3. 还可以修饰在接口上,表示当前的接口的所有实现类都应用事务。
 * 4. 也可以修饰在接口方法上,表示所有实现该方法的地方都自动应用事务。
 * 5. 事务属性
 *    propagation = Propagation.REQUIRED 表示当前运行方法必须有事务环境
 *    readOnly = false 表示读写事务
 *    noRollbackFor = ArithmeticException.class 遇到指定的异常不回滚
 */
@Service
@Transactional
public class AccountServiceImpl implements IAccountService {
    // 注入dao
    @Autowired
    private IAccountDao accountDao;

    @Override
    public void save(Account account) {
        accountDao.save(account);
        //int i=1/0;//如果没有用事务,数据库会多一条数据。有用事务,数据库不会变化。
        String str = null;str.length();
        accountDao.save(account);
    }
}

七、总结

1. Spring声明式事务原理:Aop
- 1)切面类:DataSourceTransactionManagerSpring已经提供,且针对不同的持久层实现技术,提供了不同的事务实现;
- 2)AOP 关键
  切入点表达式,只要目标对象符合切入点表达式规则,就可以声明代理对象;
  自动代理:根据目标对象有实现接口,使用JDK代理;目标对象没有实现接口,使用CGLib代理。

2. Spring声明式事务特点

优点:
- 解耦,事务控制代码与业务代码完全解耦;

  • 方便维护,假如项目不想用事务,直接移除配置;如果更换了持久层,事务控制只需要修改事务管理器即可。

缺点:
  Spring声明式事务原理是AOP,AOP原理是代理,代理只能对方法进行拦截,所以Spring的声明式事务只能对方法进行事务控制,不能对方法的某几行进行事务控制。

3. 事务粒度

  • 粗粒度事务控制:Spring声明式事务控制就是粗粒度事务控制,是方法级别的事务控制

  • 细粒度事务控制:一般都是指编程式事务控制,通过代码手动控制事务。(耦合,重复代码多)


星河滚烫,你是人间理想