Spring事务

由于Spring对持久层的很多框架都有支持 , Hibernate 、 jdbc 、JdbcTemplate , MyBatis ,由于使用的框架不同,所以使用事务管理操作API 也不尽相同。 为了规范这些操作, Spring统一定义一个事务的规范 ,这其实是一个接口 。这个接口的名称 : PlatformTrasactionManager.并且它对已经做好的框架都有支持.

如果dao层使用的是JDBC, JdbcTemplate 或者mybatis,那么 可以使用DataSourceTransactionManager 来处理事务

如果dao层使用的是 Hibernate, 那么可以使用HibernateTransactionManager 来处理事务

相关的API

PlatformTransactionManager

​ 平台事务管理器是一个接口,实现类就是Spring真正管理事务的对象。

​ 常用的实现类:

DataSourceTransactionManager   :JDBC开发的时候(JDBCTemplate,MyBatis),使用事务管理。

​ HibernateTransactionManager :Hibernate开发的时候,使用事务管理。

TransactionDefinition

​ 事务定义信息中定义了事务的隔离级别,传播行为,超时信息,只读。

TransactionStatus:事务的状态信息

​ 事务状态信息中定义事务是否是新事务,是否有保存点。

编程式(通过代码)(硬编码)事务

  • 添加依赖
<dependencies>
<!--依赖-->
<!--1. spring的核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--2. spring-jdbc的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--3. spring-test-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--10. aop的依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<!--4. mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!--5. mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!--6. mybatis-spring-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!--7. Junit的依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--8. lombok的依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<!--9. 日志的依赖-->
<!-- log start -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.6</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.6</version>
</dependency>
<!-- log end -->
</dependencies>
  • 代码
package com.jwang.service.impl;

import com.jwang.dao.AccountDao;
import com.jwang.pojo.Account;
import com.jwang.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import javax.sql.DataSource;

/**
* 包名:com.jwang.service.impl
* @author Leevi
* 日期2020-08-12 10:57
* 持久层不同的框架有不同的事务控制方案:
* 1. 原始的JDBC:
* 1.1 开启事务: connection.setAutoCommit(false);
* 1.2 提交事务: connection.commit();
* 1.3 回滚事务: connection.rollback();
*
* 2. DBUtils框架: 和原始的JDBC是一样的
* 3. mybatis框架: 底层也是使用的JDBC的事务
* 3.1 开启事务: sqlSessionFactory.openSession(); 开启事务
* 3.2 提交事务: sqlSession.commit();
* 3.3 回滚事务: sqlSession.rollback();
* 4. 只要某个持久层框架和spring整合了,那么事务相关的操作就都交给spring控制
* 4.1 编程式事务(了解)
* 4.2 声明式事务(重点掌握)
*/
@Service
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountDao accountDao;
@Autowired
private DataSource dataSource;
@Override
public void transfer(String fromName, String toName, double money) {
//创建事务管理者
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
//创建事务模板
TransactionTemplate transactionTemplate = new TransactionTemplate();

//给事务模板配置事务管理者
transactionTemplate.setTransactionManager(transactionManager);
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
//1. 转出账户扣款
accountDao.updateAccount(new Account(null,fromName,-money));

//出现异常
int num = 10/0;

//2. 转入账户收款
accountDao.updateAccount(new Account(null,toName,money));
return null;
}
});

}
}

Spring声明式事务-xml配置方式

Spring的声明式事务的作用是: 无论spring集成什么dao框架,事务代码都不需要我们再编写了

声明式的事务管理的思想就是AOP的思想。面向切面的方式完成事务的管理。声明式事务有两种,xml配置方式和注解方式.

<dependencies>
<!--依赖-->
<!--1. spring的核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--2. spring-jdbc的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--3. spring-test-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--10. aop的依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<!--4. mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!--5. mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!--6. mybatis-spring-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!--7. Junit的依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--8. lombok的依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<!--9. 日志的依赖-->
<!-- log start -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.6</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.6</version>
</dependency>
<!-- log end -->
</dependencies>
  • 配置事务管理器

    <bean id="transactionManager" 		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
    </bean>
  • 配置事务建议(事务的规则)

    <!--配置事务规则-->
    <tx:advice id="transferAdvice" transaction-manager="transactionManager">
    <!--配置事务的属性-->
    <tx:attributes>
    <!--
    哪个方法需要使用什么样的事务配置
    rollback-for="java.lang.Exception" 什么时候回滚:遇到所有的Exception都会回滚
    no-rollback-for="java.lang.NullPointerException" 什么时候不回滚:遇到NullPointerException不回滚
    timeout="-1" 事务的超时时间,默认不超时
    read-only="false" 是否是只读事务,默认不是只读事务,只读事务只会针对于查询方法,如果是增删改一定不能设置为只读
    isolation="" 事务的隔离级别: 目的是为了防止不同事务之间相互影响
    1. Read Uncommitted 读取到未提交的事务,在这种隔离级别下,可能发生脏读、不可重复读、幻读
    2. Read Committed(Oracle的默认隔离级别) 读取到已提交的事务,在这种隔离级别下,不可能发生脏读,但是可能发生不可重复读和幻读
    3. Repeatable Read(mysql的默认隔离级别) 可重复读,在这种隔离级别下不会发生脏读、不可重复读,但是有可能发生幻读
    4. Serializable 串行化的,在这种隔离级别下不会发生脏读、不可重复读、幻读

    propagation="" 事务的传播行为
    - PROPAGATION_REQUIRED:默认值,也是最常用的场景.
    如果当前没有事务,就新建一个事务,
    如果已经存在一个事务中,加入到这个事务中。

    - PROPAGATION_SUPPORTS:
    如果当前没有事务,就以非事务方式执行。
    如果已经存在一个事务中,加入到这个事务中。

    - PROPAGATION_MANDATORY
    如果当前没有有事务,就抛出异常;
    如果已经存在一个事务中,加入到这个事务中。

    保证不在同一个事务里:
    - PROPAGATION_REQUIRES_NEW
    如果当前有事务,把当前事务挂起,创建新的事务但独自执行

    - PROPAGATION_NOT_SUPPORTED
    如果当前存在事务,就把当前事务挂起。不创建事务

    - PROPAGATION_NEVER
    如果当前存在事务,抛出异常

    -->
    <tx:method name="transfer"
    rollback-for="java.lang.Exception"/>
    </tx:attributes>
    </tx:advice>
  • 配置事务的AOP

    <aop:config>
    <!--声明切入点-->
    <aop:pointcut id="pt1" expression="execution(* com.jwang.service.impl.AccountServiceImpl.transfer(..))"/>
    <!--绑定切入点和事务-->
    <aop:advisor advice-ref="transferAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>

事务的传播行为

事务的传播行为的作用

​ 我们一般都是将事务设置在Service层,那么当我们调用Service层的一个方法的时候, 它能够保证我们的这个方法中,执行的所有的对数据库的更新操作保持在一个事务中, 在事务层里面调用的这些方法要么全部成功,要么全部失败。

​ 如果你的Service层的这个方法中,除了调用了Dao层的方法之外, 还调用了本类的其他的Service方法,那么在调用其他的Service方法的时候, 我必须保证两个service处在同一个事务中,确保事物的一致性。

​ 事务的传播特性就是解决这个问题的。

事务的传播行为的取值

保证在同一个事务里面:

  • PROPAGATION_REQUIRED:默认值,也是最常用的场景.

    如果当前没有事务,就新建一个事务,
    如果已经存在一个事务中,加入到这个事务中。

  • PROPAGATION_SUPPORTS:

    如果当前没有事务,就以非事务方式执行。

    如果已经存在一个事务中,加入到这个事务中。

  • PROPAGATION_MANDATORY

    如果当前没有有事务,就抛出异常;

    如果已经存在一个事务中,加入到这个事务中。

保证不在同一个事物里:

  • PROPAGATION_REQUIRES_NEW

    如果当前有事务,把当前事务挂起,创建新的事务但独自执行

  • PROPAGATION_NOT_SUPPORTED

    如果当前存在事务,就把当前事务挂起。不创建事务

  • PROPAGATION_NEVER

    如果当前存在事务,抛出异常

spring声明式事务-注解方式

  • 在applicationContext里面打开注解驱动

    <?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:aop="http://www.springframework.org/schema/aop" 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/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">
    <import resource="classpath:application-mybatis.xml"></import>
    <!--配置申明式事务-->
    <!--
    注解方式配置声明式事务
    1. 配置事务管理者
    2. 加载事务注解驱动
    3. 在代码中使用Transactional注解来标注,哪个方法需要使用事务
    -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager"/>
    </beans>
  • 在业务逻辑类上面使用注解

    @Transactional : 如果在类上声明,那么标记着该类中的所有方法都使用事务管理。也可以作用于方法上, 那么这就表示只有具体的方法才会使用事务。