Spring 事务

目录大纲         笔记出处:哔哩哔哩视频学习


一、Spring的事务管理

        事务是一些SQL语句作为一个整体执行的集合。事务原本是数据库中的概念,在Dao层。但一般情况下,需要将事务提升到业务层,即Service层。这样做是为了能够使用事务的特性来管理具体的业务。

        在Spring中通常可以用一下两种方式实现事务的管理:

        1)使用Spring的事务注解管理事务

        2)使用AspectJ的AOP配置事务管理

二、Spring事务管理API

        Spring 的事务管理,主要用到两个事务相关的接口事务管理器接口PlatformTransactionManager和事务定义接口TransactionDefinition

1、事务管理器接口

        事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息使用Spring的事务管理器,管理不同数据库访问技术的事务处理,开发人员只需要掌握Spring的事务处理一个方案,就可以实现使用不同数据库访问技术的事务管理。事务管理器有很多实现类:一种数据库的访问技术有一个实现类,由实现类具体完成事务的提交,回滚意味着:Hibernate或者Mybatis访问数据库都有自己的事务管理器实现类

1)常用的两个实现类

        PlatformTransactionManager 接口有两个常用的实现类:
                 DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。
                 HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。

2)事务提交和回滚的时机

        当你的业务方法正常执行时,没有异常,事务是提交的。如果你的业务方法抛出了运行时异常,事务是回滚的。Spring 事务的默认回滚方式是:发生运行时异常和 Error时回滚,发生受查(编译)异常时提交。不过,对于受查异常,程序员也可以手工设置其回滚方式。

异常分类

        Error:严重错误,回滚事务

                比如 OutOfMemoryError、ThreadDeath、NoSuchMethodError

        Exception:异常类,可以抛出来的异常情况

                1)运行时异常:是 RuntimeException 类或其子类

                         例如:NullPointerException、ArrayIndexOutOfBoundsException

                 2)受查异常:编写Java代码的时候,必须要抛出来的异常

                        例如:SQLException,ClassNotFoundException,IOException

2、事务定义接口

        TransactionDefinition接口,定义了事务描述相关的三类常量,定义了有关事务控制的属性:1)事务隔离级别;2)事务传播行为;3)事务超时时限。Serivice业务方法说明的事务属性,和ACID不一样。

1)隔离级别

        控制事务之间影响的程度,隔离级别定义了个值,但只有个隔离级别

DEFAULT

采用DB默认的事务隔离级别。

MySQL默认:REPEATABLE_READ ;Oracle 默认为 READ_COMMITTED

READ_UNCOMMITED读未提交。未解决任何并发值问题
READ_COMMITTED读已提交。解决脏读,存在不可重复读与幻读
REPEATABLE_READ可重复度。解决脏读,不可重复度,存在幻读
SERIALIZABLE串行化,不存在并发问题,效率低

2)超时时间

        表示一个业务方法最长的执行时间,没有达到时间没有执行完毕,Spring回滚事务;常量 TIMEOUT_DEFAULT 定义了事务底层默认超时时限,sql 语句的执行时长。
        注意:事务的超时时限起作用的条件比较多,且超时的时间计算点较复杂。所以,该值一般就使用默认值即可。超时时间,以秒为单位,整数值,默认为-1

3)传播行为

        传播行为:业务方法在调用时,事务在方法之间的,传递和使用。所谓事务传播行为是指,处于不同事务中的方法在相互调用时,执行期间事务的维护情况。如,A 事务中的方法 doSome()调用 B 事务中的方法doOther(),在调用执行期间事务的维护情况,就称为事务传播行为,事务传播行为是加在方法上

A、PROPAGATION_REQUIRED

        指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新事务。这种传播行为是最常见的选择,也是Spring 默认的事务传播行为。如该传播行为加在 doOther()方法上。若 doSome()方法在调用 doOther()方法时就是在事务内运行的,则 doOther()方法的执行也加入到该事务内执行。若 doSome()方法在调用 doOther()方法时没有在事务内执行,则doOther()方法会创建一个事务,并在其中执行。

B、PROPAGATION_REQUIRES_NEW

        方法总是需要一个新事物。如果调用方法是,存在一个事务,则原来的事务暂停,直到新事物执行完毕。如果方法调用时,没有事务,则新建一个事务,在新事务里面执行

C、PROPAGATION_SUPPORTS

        指定的方法支持当前事务,没有事务也可以正常执行;有事务就用当前事务,没事务就没有


三、环境准备

1、建表

数据准备:

2、POM依赖

<dependencies>
    <!--lomok依赖-->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.12</version>
      <scope>provided</scope>
    </dependency>
    <!--单一测试依赖-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!--Spring 依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <!--Spring事务依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <!--MyBatis依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.1</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.9</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.12</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.5</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <resources>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <filtering>true</filtering>
      </resource>
    </resources>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

3、实体类 

4、DAO接口

5、Mapper接口

  GoodsDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC
        "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--注意 namespace 后面 跟自己的包路径-->
<mapper namespace="com.dgs.dao.GoodsDao">
    <select id="selectById"  resultType="com.dgs.entity.Goods">
        select id ,name,amount,price from goods where id = #{id}
    </select>
    <update id="updateGoods" parameterType="com.dgs.entity.Goods">
        update goods set amount = amount - #{amount} where id = #{id}
    </update>
</mapper>

  SaleDao.xml 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC
        "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--注意 namespace 后面 跟自己的包路径-->
<mapper namespace="com.dgs.dao.SaleDao">
    <insert id="insertSale">
        insert into sale (gid,nums) values (#{gid},#{nums});
    </insert>
</mapper>

6、自定义异常类

7、定义Service接口和接口实现类

public interface BuyGoodsService {
    void  buy(Integer goodsId,Integer num);
}
@Service
public class BuyGoodsServiceImpl implements BuyGoodsService {

    @Resource
    private SaleDao saleDao;

    @Resource
    private GoodsDao goodsDao;


    public void buy(Integer goodsId, Integer num) {
        // 生成销售记录
        Sale sale = new Sale();
        sale.setGid(goodsId);
        sale.setNums(num);
        saleDao.insertSale(sale);
        // 更新库存
        Goods goods = goodsDao.selectById(goodsId);
        if (goods == null) {
            throw new NullPointerException("无此商品");
        }else if(goods.getAmount() < num){
            throw new NotEnougthException("库存不足");
        }
        //更新库存
        Goods buyGoods = new Goods();
        buyGoods.setId(goodsId);
        buyGoods.setAmount(num);
        goodsDao.updateGoods(buyGoods);
    }
}

8、配置文件

 applicationContext.xml

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

    <!--声明数据源-->
    <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/dgs_study" />
        <property name="username" value="root" />
        <property name="password" value="" />
    </bean>

    <!--声明SqlSessionFactory-->
    <bean  id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="myDataSource" />
        <property name="configLocation" value="classpath:mybatis.xml" />
    </bean>

    <!--声明MapperScannerConfiguration-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="factory"/>
        <property name="basePackage" value="com.dgs.dao"/>
    </bean>
    <!--包扫描-->
    <context:component-scan base-package="com.dgs.service" />

</beans>

  mybatis.xml 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC
        "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 设置日志 -->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <!--别名-->
    <typeAliases>
        <package name="com.dgs.entity"/>
    </typeAliases>
    <!-- 指定其他mapper文件的位置-->
    <mappers>
        <package name="com.dgs.dao"/>
    </mappers>
</configuration>

9、测试类 

public class AppTest {

    @Test
    public void test() {
        String config = "applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        BuyGoodsService bean = (BuyGoodsService)ctx.getBean("buyGoodsServiceImpl");
        bean.buy(1001,1);
    }
}

四、Spring框架使用自己的注解@Transaction控制事务

通过@Transactional 注解方式,可将事务织入到相应 public 方法中,实现事务管理。

1、注解属性

位置:1)在业务方法的上面,是在public方法的上面       2)在类的上面

1、propagation

事务的传播行为,他使用的Propagation类的枚举值。例如Propagation.REQUIRED

2、isolation

表示隔离级别,使用isolation类的枚举值,表示隔离界别,默认是Isolation.DEFAULT

3、readOnly

boolean类型的值,表示数据库操作是不是只读的,默认是false

4、timeout

事务超时,默认是-1,整数值,单位是秒。例如timeout=20

5、roolbackFor

表示回滚的异常类列表,他的值是一个数组

6、roolbackForClassName

表示回滚的异常类列表,他的值是异常类名称,是String类型的值

7、noRollbackFor

不需要回滚的异常类列表。是class类型的
8、noRollbackForClassNAme不需要回滚的异常;类列表,是String类型的值

2、注解的使用步骤:

1)在配置文件:applicationContext.xml,声明事务的内容

声明事务管理器,说明使用那个事务管理器对象

声明使用注解管理事务,开启是注解驱动

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

    <!--声明数据源-->
    <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/dgs_study" />
        <property name="username" value="root" />
        <property name="password" value="" />
    </bean>

    <!--声明SqlSessionFactory-->
    <bean  id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="myDataSource" />
        <property name="configLocation" value="classpath:mybatis.xml" />
    </bean>

    <!--声明MapperScannerConfiguration-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="factory"/>
        <property name="basePackage" value="com.dgs.dao"/>
    </bean>
    <!--包扫描-->
    <context:component-scan base-package="com.dgs.service" />

    <!-- ____________声明事务的控制_____________ -->
    <!--声明事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
        <property name="dataSource" ref="myDataSource" />
    </bean>

    <!--开启事务注解驱动,声明框架使用注解管理事务 transaction-manager:指定事务管理器的id-->
    <tx:annotation-driven transaction-manager="transactionManager" />

</beans>

2)在类的源代码中,加入@Transaction

@Service
public class BuyGoodsServiceImpl implements BuyGoodsService {

    @Resource
    private SaleDao saleDao;

    @Resource
    private GoodsDao goodsDao;

    @Override
    @Transactional(
            propagation = Propagation.REQUIRED,
            isolation = Isolation.DEFAULT,
            readOnly = false,
            timeout = 20,
            rollbackFor = {NullPointerException.class,NotEnougthException.class}
    )
    public void buy(Integer goodsId, Integer num) {
        // 生成销售记录
        Sale sale = new Sale();
        sale.setGid(goodsId);
        sale.setNums(num);
        saleDao.insertSale(sale);
        // 更新库存
        Goods goods = goodsDao.selectById(goodsId);
        if (goods == null) {
            throw new NullPointerException("无此商品");
        }else if(goods.getAmount() < num){
            throw new NotEnougthException("无库存");
        }
        //更新库存
        Goods buyGoods = new Goods();
        buyGoods.setId(goodsId);
        buyGoods.setAmount(num);
        goodsDao.updateGoods(buyGoods);
    }
}

五、使用 AspectJ 的 AOP 配置管理事务

使用aspect的aop,声明事务控制叫做声明式事务

使用步骤:

1、pom.xml加入 apeing-aspect的依赖

2、在Spring的配置文件声明事务的内容

        1)声明事务管理器

        2)声明业务方法需要的业务属性

        3)声明切入点表达式

  修改配置文件applicationContext.xml

<?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 https://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 https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--声明数据源-->
    <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/dgs_study" />
        <property name="username" value="root" />
        <property name="password" value="" />
    </bean>

    <!--声明SqlSessionFactory-->
    <bean  id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="myDataSource" />
        <property name="configLocation" value="classpath:mybatis.xml" />
    </bean>

    <!--声明MapperScannerConfiguration-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="factory"/>
        <property name="basePackage" value="com.dgs.dao"/>
    </bean>
    <!--包扫描-->
    <context:component-scan base-package="com.dgs.service" />

    <!-- ____________声明事务的控制_____________ -->
    <!--1、声明事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
        <property name="dataSource" ref="myDataSource" />
    </bean>

    <!--2、声明业务方法的事务属性(隔离级别,传播行为,超时)-->
    <!--
        id:给业务方法配置事务段代码起个名称,唯一值
        transaction-manager:事务管理器的id
    -->
    <tx:advice id="serviceAdvice" transaction-manager="transactionManager">
        <!--
            给具体的业务方法增加事务的说明
            name:业务方法名称,配置name的值 1:业务方法的名称  2:带有部分通配符的方法名称 3:使用*
            propagation:指定传播行为的值
            isolation:是否只读,默认false
            timeout:超时时间
            rollback-for:指定回滚的异常类列表,使用的异常全限定类名称
        -->
        <tx:attributes>
            <tx:method name="buy" propagation="REQUIRED"
                       isolation="DEFAULT" read-only="false" timeout="20" rollback-for="java.lang.NullPointerException,com.dgs.excetion.NotEnougthException"/>
            <tx:method name="add*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/>
            <tx:method name="modify*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
            <tx:method name="remove*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
            <tx:method name="*" propagation="SUPPORTS" read-only="true" />
        </tx:attributes>
    </tx:advice>

    <!--声明切入点表达式,表示那些包中的类,类中的方法参与方法与事务-->
    <aop:config>
        <!--声明切入点表达式
            expression:切入点表达式,表示那些类和类中的方法参与事务
            id:切入点表达式的名称:唯一值
        -->
        <aop:pointcut id="servicePointcut" expression="execution(* *..service..*.*(..))"/>
        <!--管理切入点表达式和事务通知-->
        <aop:advisor advice-ref="serviceAdvice" pointcut-ref="servicePointcut"/>
    </aop:config>

</beans>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值