【Spring】Spring 事务之编程式事务

事务概述

数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性。事务是数据库运行中的逻辑工作单位,由DBMS中的事务管理子系统负责事务的处理。

事务特征(ACID)

  • 原子性:事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的。
  • 一致性:这表示数据库的引用完整性的一致性,表中唯一的主键等。
  • 隔离性:可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据损坏。
  • 持久性:一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除。

并发事务带来的问题

  • 更新丢失(Lost Update)
    多个事务修改同一行记录(都未提交), 后面的修改覆盖了前面的修改.
  • 脏读(Dirty Reads)
    一个事务可以读取另一个事务未提交的数据.
  • 不可重复读(Non-Repeatable Reads)
    同一个事务中执行两次相同的查询, 可能得到不一样的结果. 这是因为在查询间隔内,另一个事务修改了该记录并提交了事务.
  • 幻读(Phantom Reads)
    当某个事务在读取某个范围内的记录时, 另一个事务又在该范围内插入了新的记录, 当之前的事务再次读取该范围的记录时, 会产生幻行.

事务的传播特性

事务传播行为就是多个事务方法调用时,如何定义方法间事务的传播。Spring定义了7中传播行为:
(1)propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是Spring默认的选择。
(2)propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
(3)propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
(4)propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
(5)propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
(6)propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
(7)propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作。

事务的隔离级别

在MySQL常用的存储引擎中, 只有InnoDB支持事务, 所以这里说的隔离级别指的是InnoDB下的事务隔离级别.

READ UNCOMMITTED(读未提交)
在该隔离级别, 事务中的修改即使没有提交, 对其他事务也都是可见的. 避免了更新丢失的发生.
READ COMMITTED(读已提交)
在该隔离级别, 一个事务只能看见已经提交的事务所做的修改. 避免了更新丢失和脏读.
REPEATABLE READ(可重复读)
MySQL默认的隔离级别, 该级别保证了在同一个事务中多次读取同样的记录的结果是一致的. 避免了更新丢失、脏读、不可重复读和幻读. (注意看MySQL官网, RR隔离级别下解决了幻读问题)
SERIALIZABLE(可串行化)
SERIALIZABLE是最高的隔离级别, 它通过强制事务串行化执行, 避免了并发事务带来的问题.

事务几种实现方式

(1)编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。
(2)基于 TransactionProxyFactoryBean的声明式事务管理
(3)基于 @Transactional 的声明式事务管理
(4)基于Aspectj AOP配置事务

基于底层 API 的编程式事务管理

第一步:创建maven工程和数据库表

打开mysql,创建一张user

CREATE TABLE user(
   ID   INT NOT NULL AUTO_INCREMENT,
   NAME VARCHAR(20) NOT NULL,
   AGE  INT NOT NULL,
   PRIMARY KEY (ID)
);

MYISAM不支持事务,INNODB支持事务处理,Mysql版本从5.5.8开始,默认使用INNODB存储引擎,mysql创建数据库时应选择InnoDB
在这里插入图片描述

创建一个maven工程【Spring】Spring入门案例

pom.xml中加入Springmysql相关的包

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.2.7.RELEASE</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.2.7.RELEASE</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.7.RELEASE</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>5.2.7.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>5.2.7.RELEASE</version>
  </dependency>

  <dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
  </dependency>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.20</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.7.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.2.7.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.2.7.RELEASE</version>
  </dependency>
</dependencies>

第二步:添加数据库处理事务


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

@Repository("userTransaction")
public class UserTransaction {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private DataSourceTransactionManager txManager;
 
    public void updateUser() {
        DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
        TransactionStatus status = txManager.getTransaction( transactionDefinition);
        try {
            String sql = "insert  into  user values (?,?,?)";
            String sql1 = "insert  into  user values (?,?,?)";
            Object[] para = {1, "张三", 21};
            jdbcTemplate.update("delete from user");
            jdbcTemplate.update(sql, para);
            jdbcTemplate.update(sql1, para);
            txManager.commit(status);
        } catch (Exception e) {
            txManager.rollback(status);
            System.out.println("回滚事务" + e.getMessage());
            e.printStackTrace();
        }

    }
}

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"
       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">
    <context:component-scan base-package="com.lucas"/>
    <!-- 1.配置数据源 -->
    <bean id="dataSource"
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <!-- 1.1.数据库驱动 -->
        <property name="driverClassName"
                  value="com.mysql.cj.jdbc.Driver"></property>
        <!-- 1.2.连接数据库的url -->
        <property name="url"
                  value="jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&amp;useSSL=false&amp;serverTimezone=UTC&amp;rewriteBatchedStatements=true"></property>
        <!-- 1.3.连接数据库的用户名 -->
        <property name="username" value="root"></property>
        <!-- 1.4.连接数据库的密码 -->
        <property name="password" value="123456"></property>
    </bean>

    <!-- 2配置JDBC模板 -->
    <bean id="jdbcTemplate"
          class="org.springframework.jdbc.core.JdbcTemplate">
        <!-- 默认必须使用数据源 -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--    配置数据源的事务管理-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
 
</beans>

第三步:添加测试代码

public class App {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserTransaction userTransaction = (UserTransaction) applicationContext.getBean("userTransaction");
        userTransaction.updateUser();
    }
}

运行结果:
从执行的结果来看,由于发生异常导致代码回滚,数据库中并没有插入数据
在这里插入图片描述
在这里插入图片描述

基于 TransactionTemplate 的编程式事务管理

基于 TransactionTemplate 的编程式事务管跟前面的基于API事务使用区别不大

applicationContext.xml中添加bean

    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="txManager"/>
    </bean>

修改事务处理代码 ,执行结果同上

package com.lucas;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

@Repository("userTransaction")
public class UserTransaction {
    @Autowired
    private JdbcTemplate jdbcTemplate;
     @Autowired
    private TransactionTemplate transactionTemplate;
 
    public void updateUser1() {
        transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                String result = "";
                try {
                    String sql = "insert  into  user values (?,?,?)";
                    String sql1 = "insert  into  user values (?,?,?)";
                    Object[] para = {1, "张三", 21};
                    jdbcTemplate.update(sql, para);
                    jdbcTemplate.update(sql1, para);
                }catch (Exception e){
                    System.out.println("回滚事务" + e.getMessage());
                    status.setRollbackOnly();
                }

                return result;
            }
        });

    }
}

猜你喜欢

转载自blog.csdn.net/huweiliyi/article/details/107742414