spring实现事务管理

事务(Transaction)是面向关系型数据库(RDBMS)企业应用程序的重要组成部分,用来确保数据的完整性和一致性。

  • 事务的ACID 特性

原子性(Atomicity):一个事务是一个不可分割的工作单位,事务中包括的动作要么都做要么都不做。
一致性(Consistency):事务必须保证数据库从一个一致性状态变到另一个一致性状态,一致性和原子性是密切相关的。
隔离性(Isolation):一个事务的执行不能被其它事务干扰,即一个事务内部的操作及使用的数据对并发的其它事务是隔离的,并发执行的各个事务之间不能互相打扰。
持久性(Durability):持久性也称为永久性,指一个事务一旦提交,它对数据库中数据的改变就是永久性的,后面的其它操作和故障都不应该对其有任何影响。

  • spring事务的传播属性 Propagation

REQUIRED:
使用当前的事务,如果当前没有事务,则自己新建一个事务,子方法是必须运行在一个事务中的;如果当前存在事务,则加入这个事务,成为一个整体。
举例:领导没饭吃,我有钱,我会自己买了自己吃;领导有的吃,会分给你一起吃。
SUPPORTS:
如果当前有事务,则使用事务;如果当前没有事务,则不使用事务。
举例:领导没饭吃,我也没饭吃;领导有饭吃,我也有饭吃。
MANDATORY:
该传播属性强制必须存在一个事务,如果不存在,则抛出异常
举例:领导必须管饭,不管饭没饭吃,我就不乐意了,就不干了(抛出异常)
REQUIRES_NEW:
如果当前有事务,则挂起该事务,并且自己创建一个新的事务给自己使用;如果当前没有事务,则同 REQUIRED
举例:领导有饭吃,我偏不要,我自己买了自己吃
NOT_SUPPORTED:
如果当前有事务,则把事务挂起,自己不适用事务去运行数据库操作
举例:领导有饭吃,分一点给你,我太忙了,放一边,我不吃
NEVER:
如果当前有事务存在,则抛出异常
举例:领导有饭给你吃,我不想吃,我热爱工作,我抛出异常
NESTED:
如果当前有事务,则开启子事务(嵌套事务),嵌套事务是独立提交或者回滚;如果当前没有事务,则同 REQUIRED。但是如果主事务提交,则会携带子事务一起提交。如果主事务回滚,则子事务会一起回滚。相反,子事务异常,则父事务可以回滚或不回滚。
举例:领导决策不对,老板怪罪,领导带着小弟一同受罪。小弟出了差错,领导可以推卸责任。

  • spring中的事务隔离级别

isolation-default 这是platformTransactionManager默认的事务隔离级别,使用数据库默认的事务隔离级别/另外4个与JDBC的事务隔离级别相对应
isolation-read_uncommitted
isolation-read_committed
isolation-repeatable-read
isolation-serializable

  • Spring 的事务管理有 2 种方式:

1)传统的编程式事务管理,即通过编写代码实现的事务管理;
2)基于 AOP 技术实现的声明式事务管理。

本文是用声明式事务管理

  • 是什么?

通过 AOP 实现的,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

  • 优点?

不需要通过编程的方式管理事务,可以将业务逻辑代码和事务管理代码很好的分开。

  • 声明式事务管理主要有 2 种方式:

基于 XML 方式的声明式事务管理。
通过 Annotation 注解方式的事务管理。

下面介绍如何通过 XML 的方式实现声明式事务管理

  • pom依赖
<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.5</version>
        </dependency>
        <!--Spring事物依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.3.5</version>
        </dependency>
  • spring-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">

    <!-- 开启扫描注解 -->
    <context:component-scan base-package="com.ljw"></context:component-scan>
    <!-- 引入外部properties文件 -->
    <context:property-placeholder location="classpath:druid.properties"></context:property-placeholder>
    <!-- 通过druid连接池获取数据源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!--数据库驱动 -->
        <property name="driverClassName" value="${driver}"></property>
        <!--连接数据库的url -->
        <property name="url" value="${url}"></property>
        <!--连接数据库的用户名 -->
        <property name="username" value="${user}"></property>
        <!--连接数据库的密码 -->
        <property name="password" value="${pwd}"></property>
    </bean>
    <!-- 配置JDBC模板,使用JdbcTemplate类 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--默认必须使用数据源 引用上面的数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 编写通知:对事务进行增强(通知),需要编写切入点和具体执行事务的细节 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 给切入点方法添加事务详情,name表示方法名称,*表示任意方法名称,
            propagation用于设置传播行为,read-only表示隔离级别,是否只读 -->
            <tx:method name="*" propagation="REQUIRED" read-only="false"/>
        </tx:attributes>
    </tx:advice>
    <!-- aop编写,让Spring自动对目标生成代理,需要使用AspectJ的表达式 -->
    <aop:config proxy-target-class="true">
        <!-- 切入点,execution定义的表达式表示com.ljw.service.impl包下的所有类所有方法都应用该事务 -->
        <aop:pointcut id="point" expression="execution(* com.ljw.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="point"></aop:advisor>
    </aop:config>
  • bean类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Goods {
    private Integer goodId;
    private Integer typeId;
    private String goodName;
    private Integer goodNum;
    private Float goodPrice;


    public Goods(Integer typeId, String goodName, Integer goodNum, Float goodPrice) {
        this.typeId = typeId;
        this.goodName = goodName;
        this.goodNum = goodNum;
        this.goodPrice = goodPrice;
    }
  • dao层实现
@Controller
public class GoodsDaoImpl implements GoodsDao {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Override
    public List<Goods> findAll() {
        String sql = "select * from goods";
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Goods.class));
    }

    @Override
    public Integer save(Goods goods) {
        String sql = "INSERT INTO goods VALUES(NULL,?,?,?,?)";
        return jdbcTemplate.update(sql,goods.getTypeId(),goods.getGoodName(),goods.getGoodNum(),goods.getGoodPrice());
    }


    @Override
    public Integer update(Goods goods) {
        String sql = "UPDATE goods SET type_id=?,good_name=?,good_num=?,good_price=? WHERE good_id=?";
        return jdbcTemplate.update(sql,goods.getTypeId(),goods.getGoodName(),goods.getGoodNum(),goods.getGoodPrice(),goods.getGoodId());
    }

    @Override
    public Integer delete(Integer id) {
        String sql = "delete from goods where good_id = ?";
        return jdbcTemplate.update(sql,id);
    }
}
  • service层实现
@Service
public class GoodsServiceImpl implements GoodsService {
    @Autowired
    GoodsDao goodsDao;
    @Override
    public List<Goods> findAll() {
        return goodsDao.findAll();
    }

    @Override
    public Integer saveAndUpdate(Goods goods,Goods goods2) {
        Integer rows = goodsDao.save(goods);
        Integer rows2 = goodsDao.update(goods2);
        return rows + rows;
    }


    @Override
    public Integer delete(Integer id) {
        return goodsDao.delete(id);
    }

}
  • 测试
@Test
    public void save() {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        GoodsService goodsService = context.getBean("goodsServiceImpl", GoodsServiceImpl.class);
        //添加的对象
        Goods goods = new Goods(3, "test88", 2, 2f);
        //修改的对象
        Goods goods2 = new Goods(1, 9, "test", 2, 2f);
        int rows = goodsService.saveAndUpdate(goods, goods2);
        System.out.println(rows);
    }

添加和修改的方法要同时成功,否则事务回滚

注解 的方式实现声明式事务管理

  • spring–xml文件
<!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 通过注解加事务 -->
	<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"></tx:annotation-driven>
  • 只用在service层实现中加上注解@Transactional
@Transactional
public class GoodsServiceImpl implements GoodsService {
	//towork
}