Spring的事务管理详解

本文详细介绍Spring框架中的事务管理,包括自定义事务管理器的编程式和声明式事务控制方法,以及Spring框架提供的声明式事务编程支持。此外,还介绍了事务传播属性、隔离级别的概念及配置方法。

Spring的事务管理详解

1、自定义的事务管理器

1.1 编程式事务

  • 通过在业务层中注入事务管理器对象。然后通过编码的方式进行事务的控制
  • 具体的配置
<!-- 使用DruidDataSource生成数据源,然后将其注入到spring容器中   -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</bean>

<!--    数据源事务管理-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--  注入数据源对象      -->
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 编程式事务的方式:要给每一个接口都要显示的写上每一个事务管理   -->
<!-- 将实现类 StudentServiceImpl  注入到spring容器中 ,使用的是编程式事务 -->
<bean id="studentService" class="com.liu.sql.service.StudentServiceImpl">
    <!-- 这是使用的是ref的方式直接将spring容器中的mapper接口的组件注入给StudentServiceImpl的属性       -->
    <property name="studentMapper" ref="studentMapper"/>
    <!--   注入事务管理器  -->
    <property name="platformTransactionManager" ref="transactionManager"/>
</bean>
  • 业务类代码
    private PlatformTransactionManager platformTransactionManager;

    public void setPlatformTransactionManager(PlatformTransactionManager platformTransactionManager) {
        this.platformTransactionManager = platformTransactionManager;
    }

	@Override
    public void save(Student student) {
        // 创建事务配置对象
        DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
        // 获取事务状态
        TransactionStatus status = platformTransactionManager.getTransaction(transactionDefinition);
        try {
            studentMapper.save(student);
            // 提交事务
            platformTransactionManager.commit(status);
        } catch (TransactionException e) {
            e.printStackTrace();
            // 回滚事务操作
            platformTransactionManager.rollback(status);
        } finally {
            System.out.println("执行完毕!");
        }
    }

1.2 声明式事务

  • 通过利用aop切面编程进行事务控制,并对事务属性在配置文件中完成细粒度的配置(精确到增删改查的每个详细部分),这种方式称为声明式事务
  • 优势: 通用,减少代码冗余,更加关注于业务层的逻辑开发,无需重复进行编码。
  • 环绕通知接口代码
/**
 * 声明式:自定义事务管理器(环绕通知)
 */
public class TransactionAdvice implements MethodInterceptor {

    private PlatformTransactionManager platformTransactionManager;

    public void setPlatformTransactionManager(PlatformTransactionManager platformTransactionManager) {
        this.platformTransactionManager = platformTransactionManager;
    }

    /**
     * 编剧环绕通知,然后将其注入到spring容器中
     * @param methodInvocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        // 创建事务配置对象
        DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
        // 获取事务状态
        TransactionStatus status = platformTransactionManager.getTransaction(transactionDefinition);

        try {
            // 执行具体业务方法(放行具体的业务方法)
            Object result = methodInvocation.proceed();
            // 提交事务
            platformTransactionManager.commit(status);
            return result;
        } catch (Exception exception) {
            // 回滚事务
            platformTransactionManager.rollback(status);
            exception.printStackTrace();
        }
        return null;
    }
}

  • 注册组件xml
<!-- 将实现类 StudentServiceImpl  注入到spring容器中 ,使用的是编程式事务 -->
<bean id="studentService" class="com.liu.sql.service.StudentServiceImpl">
    <!-- 这是使用的是ref的方式直接将spring容器中的mapper接口的组件注入给StudentServiceImpl的属性       -->
    <property name="studentMapper" ref="studentMapper"/>
</bean>

<!--  声明式事务的配置  -->
<!--  注册环绕通知  -->
<bean id="transactionAdvice" class="com.liu.sql.advice.TransactionAdvice">
    <!--     注入事务管理器对象   -->
    <property name="platformTransactionManager" ref="transactionManager"/>
</bean>

<!-- 配置aop切面   -->
<aop:config>
    <!--   配置切入点  within表示在指定的包中   -->
    <aop:pointcut id="pc" expression="within(com.liu.sql.service.*ServiceImpl)"/>
    <!--   切面 = 通知 + 切入点     -->
    <aop:advisor advice-ref="transactionAdvice" pointcut-ref="pc"/>
</aop:config>
  • 业务层代码实现
/**
* 声明式事务处理
*
* @param student
*/
@Override
public void save(Student student) {
    studentMapper.save(student);
    int a = 1 / 0;
    System.out.println("执行完毕!");
}

2. Spring框架开发声明式事务编程

  • 依赖
<!--   spring开发的声明式事务依赖    -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>
  • 解析
a) spring框架提供的是 <tx:advice> 标签
 作用:
    1)可以指定的事务管理器在工厂中创建一个基于事务的环绕通知对象
         <tx:advice id="注册在spring容器中的id(可以自定义名字)" transaction-manager="事务管理器对象id"/>
         id:基于事务管理器对象创建的环绕通知对象在工厂中的唯一标识
    2<tx:advice>标签可以对事务进行细粒度的控制

    注意:该<tx:advice>标签直接等价于前面子自定义的声明式事务的实现,也就是等价于下面的代码部分
// 实现类
public class TransactionAdvice implements MethodInterceptor {

    private PlatformTransactionManager platformTransactionManager;

    public void setPlatformTransactionManager(PlatformTransactionManager platformTransactionManager) {
        this.platformTransactionManager = platformTransactionManager;
    }

    /**
                 * 编剧环绕通知,然后将其注入到spring容器中
                 *
                 * @param methodInvocation
                 * @return
                 * @throws Throwable
                 */
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        // 创建事务配置对象
        DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
        // 获取事务状态
        TransactionStatus status = platformTransactionManager.getTransaction(transactionDefinition);

        try {
            // 执行具体业务方法(放行具体的业务方法)
            Object result = methodInvocation.proceed();
            // 提交事务
            platformTransactionManager.commit(status);
            return result;
        } catch (Exception exception) {
            // 回滚事务
            platformTransactionManager.rollback(status);
            exception.printStackTrace();
        }
        return null;
    }
}
<!-- 配置文件的设置  -->
<!--  注册环绕通知  -->
<bean id="transactionAdvice" class="com.liu.sql.advice.TransactionAdvice">
    <!--     注入事务管理器对象   -->
    <property name="platformTransactionManager" ref="transactionManager"/>
</bean>
  • 事务传播属性
事务传播:值在多个业务层之间互相调用时传递事务的过程称之为事务传播
        将事务对象在业务层之间进行传递的过程
  propagation:事务传播属性的设置
      REQUIRED:需要事务                  如果外部没有事务 则开启新的事务 如果外部存在事务,则融入当前事务
      SUPPORTS:支持事务(一般用于查询)      如果外部没有事务 不会开启新的事务 如果外部存在事务,则融入当前事务
      REQUIRES_NEW:每次都开始新的事务      如果外部存在事务,外层事务进行挂起,自己开启新的事务执行。执行完成,恢复外层事务继续执行
      NOT_SUPPORTED:不支持事务           如果外部存在事务,外层事务进行挂起,自己以非事务执行。执行完成,恢复外层事务继续执行
      NEVER:不能有事务           存在事务就会出错
      MANDATORY:强制事务             不存在事务就会报错
      NESTED:嵌套事务             事物之间可以嵌套运行
  • 事务的隔离级别
isolation:事务的隔离级别
	DEFAULT:  使用数据库默认的隔离级别(推荐)
	READ_UNCOMMITTED: 读未提交  一个客户端能读取到另一个客户端没有提交的数据  脏读现象
	READ_COMMITTED: 读已提交   一个客户端只能读取到已经提交的数据          避免脏读现象
	REPEATABLE_READ: 可重复读  主要用来避免不可重复读的现象出现            行锁 mysql
	SERIALIZABLE: 序列化读     主要用来避免幻读现象出现                   表锁
注意:隔离级别越高,查询效率越低,一般推荐使用默认的数据库隔离级别
  • tx标签的具体使用方式
b)具体的配置使用
<!--    spring开发的声明式事务框架,开始声明式事务(环绕通知的方式),这一句话等价于
                环绕通知的实现类 + 注册环绕通知  这两个部分的内容
                <bean id="transactionAdvice" >
            <tx:advice id="注册在spring容器中的id(可以自定义名字)" transaction-manager="事务管理器对象id"/>

            <bean id="" transaction-manager="">
                id:基于事务管理器对象创建的环绕通知对象在工厂中的唯一标识
            作用:
                1)根据指定的业务管理器在工厂中创建一个事务的环绕通知对象
                2)对业务层方法进行细粒度的事务管理
-->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
    <!--
         事务细粒度配置
         name:表示类中的方法名,*通配符,*save*表示类中的方法名包含save就使用事务控制
    -->
     <tx:attributes>
         <tx:method name="save*" propagation="REQUIRED" isolation="REPEATABLE_READ"/>
         <tx:method name="add*" propagation="REQUIRED"/>
         <tx:method name="insert*" propagation="REQUIRED"/>
         <tx:method name="delete*" propagation="REQUIRED"/>
         <tx:method name="update*" propagation="REQUIRED"/>
         <!--    对于查询方法都是用support属性,目的是为了传播事务,为了实现多事务之下的事务融合特性        -->
         <tx:method name="find" propagation="SUPPORTS"/>
     </tx:attributes>
</tx:advice>

<!-- 配置aop切面   -->
<aop:config>
    <!--   配置切入点  within表示在指定的包中   -->
    <aop:pointcut id="pc" expression="within(com.liu.sql.service.*ServiceImpl)"/>
    <!--   切面 = 通知 + 切入点     -->
    <aop:advisor advice-ref="transactionAdvice" pointcut-ref="pc"/>
</aop:config>
  • 注解式事务 @Transactional
@Transactional:
	- 作用:用来给类中的方加入事务控制,简化配置文件,该注解等价于上面的两个标签: <tx:advice> + <aop:config> 两个部分相加,所以以后写上该注解,就不要再写上面的配置文件了
	- 修饰范围:类   方法   局部优先原则
		1. 加在类上,  代表类中的所有方法加入事务控制
		2. 加在方法上。代表当前方法加入事务控制
		3. 类和方法上同时存在   方法优先

使用的要求:
	- 如果要让@Transactional这个注解生效,在配置文件中需要加入如下配置
	- 查询方法显示的配置事务的传播属性为 SPUPPORT 方式
- 数据源
<!--    数据源事务管理-->
<bean id="transactionManager" 		  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--  注入数据源对象      -->
        <property name="dataSource" ref="dataSource"/>
</bean>
- <!-- 开启注解式事务生效   -->
 <tx:annotation-driven transaction-manager="transactionManager"/>

3. SM最终整合步骤 applicationContext.xml

# a、创建数据源对象
<!-- 使用DruidDataSource生成数据源,然后将其注入到spring容器中   -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</bean>

# b、创建sqlSessionFactory对象
<!-- 将sqlSessionFactoryBean注入到spring容器中。从而获取到sqlSessionFactory,目的就是为了生成sqlSession   -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 注入数据源       -->
    <property name="dataSource" ref="dataSource"/>
    <!--注入mapper配置文件 (指定mapper映射文件的位置,从而完成数据库的操作,所以mybatis配置文件中配置mapper文件位置的xml文件都可以直接进行删除,不再需要)-->
    <property name="mapperLocations" value="classpath:mappers/*.xml"/>
    <!--给实体类取别名,默认的别名:类名首字母小写  -->
    <property name="typeAliasesPackage" value="com.liu.sql.entity"/>
</bean>

# c、创建dao对象
<!-- 一次性创建mapper包下所有的mapper(dao)接口,并将其注入到spring容器中   -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!--    将 sqlSessionFactory注入到属性中   -->
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    <!--    将指定包下的所有mapper接口一次性扫描并将其生成对应的组件注入到spring容器中    -->
    <property name="basePackage" value="com.liu.sql.mapper"/>
</bean>

# d、创建事务管理器
<!--    数据源事务管理-->
<bean id="transactionManager" 	               class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--  注入数据源对象      -->
    <property name="dataSource" ref="dataSource"/>
</bean>

# e、根据事务管理器创建事务环绕通知 tx:advice
<!--
            事务细粒度配置
            name:表示类中的方法名,*通配符,*save*表示类中的方法名包含save就使用事务控制

            事务传播:值在多个业务层之间互相调用时传递事务的过程称之为事务传播
                    将事务对象在业务层之间进行传递的过程
            propagation:事务传播属性的设置
                REQUIRED:需要事务                  如果外部没有事务 则开启新的事务 如果外部存在事务,则融入当前事务
                SUPPORTS:支持事务(一般用于查询)      如果外部没有事务 不会开启新的事务 如果外部存在事务,则融入当前事务
                REQUIRES_NEW:每次都开始新的事务      如果外部存在事务,外层事务进行挂起,自己开启新的事务执行。执行完成,恢复外层事务继续执行
                NOT_SUPPORTED:不支持事务           如果外部存在事务,外层事务进行挂起,自己以非事务执行。执行完成,恢复外层事务继续执行
                        NEVER:不能有事务           存在事务就会出错
                    MANDATORY:强制事务             不存在事务就会报错
                       NESTED:嵌套事务             事物之间可以嵌套运行
        -->
<tx:attributes>
    <tx:method name="save*" propagation="REQUIRED"/>
    <tx:method name="add*" propagation="REQUIRED"/>
    <tx:method name="insert*" propagation="REQUIRED"/>
    <tx:method name="delete*" propagation="REQUIRED"/>
    <tx:method name="update*" propagation="REQUIRED"/>
    <!--    对于查询方法都是用support传播属性,为了实现多事务之下的事务融合特性        -->
    <tx:method name="find" propagation="SUPPORTS"/>
</tx:attributes>

# f、配置事务切面
<!-- 配置aop切面   -->
<aop:config>
    <!--   配置切入点  within表示在指定的包中   -->
    <aop:pointcut id="pc" expression="within(com.liu.sql.service.*ServiceImpl)"/>
    <!--   切面 = 通知 + 切入点     -->
    <aop:advisor advice-ref="transactionAdvice" pointcut-ref="pc"/>
</aop:config>







评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值