spring5 事务

1.事务介绍

1.1 事务添加到javaEE的service

1.2 声明式事务和编程试

1.3 声明式

  xml 事务开发

  注解方式

1.4 在Spring中进行事务开发底层用的aop原理

1.5 Spring事务管理Api

 

2. 步骤

1 注解配置

2.1 开启事务管理器

2.2 开始事务注解

2.3 在service层中加入,可以加载类上面,也可以加在方法上面

 

<?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:context="http://www.springframework.org/schema/context"
       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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:component-scan base-package="com.demo">

    </context:component-scan>

<!--    引入外部属性文件-->
    <context:property-placeholder location="classpath*:jdbc.properties"/>

    <!--    数据库连接池-->
    <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${prop.driverClassName}"></property>
        <property name="url" value="${prop.url}"/>
        <property name="username" value="${prop.username}"/>
        <property name="password" value="${prop.password}"/>
    </bean>

    <!--    jdbc模板对象-->

    <bean id="jdbc" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="druid"></property>
    </bean>

    <!--    开启事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="druid"></property>
    </bean>
    <!--    开启注解-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

</beans>

 

package com.demo.service;

import com.demo.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class UserService {
    @Autowired
    private UserDao userDao;
    public void account(){
        userDao.addMoney();
        int a = 10/0;
        userDao.reduceMoney();
    }
}

 2 xml配置

  1. 配置通知
  2. 配置切入点,切面
<?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:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd 
                           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.demo">

    </context:component-scan>

<!--    引入外部属性文件-->
    <context:property-placeholder location="classpath*:jdbc.properties"/>

    <!--    数据库连接池-->
    <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${prop.driverClassName}"></property>
        <property name="url" value="${prop.url}"/>
        <property name="username" value="${prop.username}"/>
        <property name="password" value="${prop.password}"/>
    </bean>

    <!--    jdbc模板对象-->

    <bean id="jdbc" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="druid"></property>
    </bean>

    <!--    开启事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="druid"></property>
    </bean>

    <!--    配置通知-->
    <tx:advice id="txadvice">
        <tx:attributes>
            <tx:method name="account" propagation="REQUIRED" isolation="REPEATABLE_READ" />
        </tx:attributes>
    </tx:advice>
    <!--    配置切入点和切面-->
    <aop:config>
        <aop:pointcut id="pt" expression="execution(* com.demo.service.UserService.*(..))"/>
        <aop:advisor advice-ref="txadvice" pointcut-ref="pt"></aop:advisor>
    </aop:config>
</beans>

 

 

3 事务传播

REQUIRED(默认的传播机制):

  • 如果当前没有事务,则新建事务
  • 如果当前存在事务,则加入当前事务,合并成一个事务

REQUIRES_NEW:

  • 新建事务,如果当前存在事务,则把当前事务挂起

NESTED

  • 如果当前没有事务,则新建事务
  • 如果当前存在事务,则创建一个当前事务的子事务(嵌套事务),子事务不能单独提交,只能和父事务一起提交。

REQUIRED

一个类的A方法调用另一个类的B方法。
假设在A方法存在一个当前事务,B方法的事务传播机制为REQUIRED,则B方法会合并到A方法的事务里执行。

A、B任意一个方法异常(默认是RuntimeException和Error)都会导致A、B的操作被回滚。

Spring事务管理器不会吞异常。
B异常后会抛给A,A如果没有catch这个异常,会继续向上抛。如果A catch住了,Spring事务管理器会替A向上抛一个UnexpectedRollbackException。总之,一旦A、B回滚,A的调用方一定能收到一个异常感知到回滚。

REQUIRES_NEW

一个类的A方法调用另一个类的B方法。
假设在A方法存在一个当前事务,B方法的事务传播机制为REQUIRES_NEW,则B方法会新建一个事务并把A所在的事务挂起。A事务等到B事务执行完后,恢复执行。

这种传播机制下,需要小心死锁问题。A事务被挂起了,如果B事务要加的锁被A占用了就会发生死锁。

  • 如果B发生异常,B事务一定回滚,B的异常随后会抛给A,如果A catch住了这个异常,A不会回滚,否则A也会回滚。
  • 如果A发生异常,则只会回滚A,不会回滚B。

NESTED

一个类的A方法调用另一个类的B方法。
假设在A方法存在一个当前事务,B方法的事务传播机制为NESTED,则B方法会作为A方法所在事务的一个子事务执行。

子事务的底层实现:B方法执行前会在A所在事务中创建一个savepoint,B异常后回滚到此savepoint。

  • 如果B异常,B一定回滚,B的异常随后会抛给A,如果A catch住了这个异常,A不会回滚,否则A也会回滚。这种情况和REQUIRES_NEW一样。
  • 如果A发生异常,则A、B都会回滚。
posted @ 2022-08-21 13:20  写代码的小哥哥  阅读(46)  评论(0)    收藏  举报