第十一篇 Spring事务

什么是事务

在java应用中,事务就是把多个操作数据库单元捆绑在一起,看看做一个整体。处于逻辑单元中所有操作要么全部成功,要么全部失败!

比如: 在银行转账业务中,A用户向B用户转钱,扣A用户金额,其次B用户加上指定金额。转账就是一个完整的事务,如果执行sql操作失败,事务回滚到开启之前状态。

事务的四大特性-----ACID原则

事务是数据库操作的基本单位,具有四个核心特性(ACID),确保数据操作的可靠性和一致性:

原子性(Atomicity)

事务被视为不可分割的最小单元,要么全部执行成功,要么全部失败回滚。例如,转账操作中扣款和收款必须同时成功或同时撤销。

一致性(Consistency)

事务执行前后,数据库必须从一个一致状态转移到另一个一致状态。例如,转账前后账户总金额应保持不变。

隔离性(Isolation)

多个事务并发执行时,一个事务的操作不应影响其他事务。数据库通过锁机制或MVCC(多版本并发控制)实现隔离级别(如读未提交、读已提交、可重复读、串行化)。

持久性(Durability)

事务一旦提交,其对数据的修改将永久保存在数据库中,即使系统崩溃也不会丢失。通常通过预写日志(WAL)技术实现。

实际应用示例

在银行系统中,ACID特性确保转账操作的安全:原子性避免部分操作失败导致数据不一致;隔离性防止并发转账时的数据冲突;持久性保证转账记录永久保存。

Mysql事务

在MySQL中,默认情况下每条SQL语句都作为一个独立的事务执行。如需手动控制事务管理,需要遵循以下操作流程:

  1. 开启事务:
sql 复制代码
START TRANSACTION;
  1. 执行事务中的SQL操作

  2. 提交事务:

sql 复制代码
COMMIT;
  1. 回滚事务(如需要):
sql 复制代码
ROLLBACK;

SQL案例

sql 复制代码
#模拟赚钱

start transaction;
# 修改指定用户金额
update t_user set money=money - 1000 where username='ykj';
update t_user set money=money + 1000 where usernam='root';

#提交事务
commit

#回滚事务
rollback;

JDBC事务管理

在jdbc中,默认情况下,发送一条sql语句就是一个事务,如果需要手动控制或者管理事务,则需要通过Connection 对象进行操作。

并且一个事务中多条sql数据必须使用同一个Connection对象,否则事务失效。

通过Connection对象中 setAutoCommit(booean flag); 默认情况下flag参数值为true,表示一条sql就是一个事务。如果需要手动控制事务则,必须设置改方法的参数为false。

commit()方法:表示提交事务

rollback():回滚事务

JDBC中如何管理事务:

java 复制代码
//1.获取Connection对象

try{

	//2.开启事务

	//3.执行事务中 逻辑单元(多条sql语句)

	//4.提交事务

}catch(Exception e){

	//捕获异常

	//5.回滚事务

}finally{

	//关闭资源

}

案例

核心配置文件开启扫描包即可

1.添加依赖

java 复制代码
<!--    导入mysql驱动包-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.30</version>
    </dependency>

2.编写接口和实现类

UserService接口:

java 复制代码
public interface UserService {
    //用户转账方法
    void transactionAccount(String fromName,String toName,double money) throws SQLException;
}

UserServiceImpl实现类

java 复制代码
@Service
public class UserServiceImpl implements UserService {

    /**
     * 用户转账的方法
     * @param fromName  用户名1
     * @param toName  用户名2
     * @param money 转账金额
     */
    @Override
    public void transactionAccount(String fromName, String toName, double money) throws SQLException {
        //1.获取连接对象
        Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/class118?serverTimezone=GMT",
                "root", "123456");
        try{
            //2.开启事务
            con.setAutoCommit(false);

            //3.执行事务业务逻辑 (转账逻辑)
            String sql = "update t_user set money = money + ? where username = ?";
            //4.获取预处理对象
            PreparedStatement ps = con.prepareStatement(sql);
            //5.设置占位符参数
            ps.setDouble(1, -money);
            ps.setString(2, fromName);
            //6.执行sql
            ps.executeUpdate();

//            int i = 10 / 0;
            //第2条sql
            String sql2 = "update t_user set money = money + ? where username = ?";
            PreparedStatement ps2 = con.prepareStatement(sql2);
            ps.setDouble(1, money);
            ps.setString(2, toName);
            ps.executeUpdate();

            //提交事务
            con.commit();
        }catch (Exception e){
            e.printStackTrace(); //打印异常信息
            //回滚事务
            con.rollback();
        }
    }
}

3.UserController类

java 复制代码
@Controller
public class UserController {

    @Autowired
    private UserService userService;
    /**
     * 用户转账方法
     */
    public void transactionAccount(String fromName, String toName, double money) throws SQLException {
        userService.transactionAccount(fromName, toName, money);
    }
}

4.测试类

java 复制代码
public class UserControllerTest {
    public static void main(String[] args) throws SQLException {
        //1.加载spring核心配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.获取UserController对象
        UserController userController = (UserController) context.getBean("userController");
        //调用转账方法
        userController.transactionAccount("zhangsan", "lisi", 1000);
    }
}

Spring整合Mybatis框架

整合思路:

MyBatis 的核心组件(包括数据库连接池对象、SqlSessionFactory、SqlSession 以及 Mapper 代理对象)都需要交由 Spring 框架统一管理。

案例

创建数据库表

创建数据库表

sql 复制代码
 CREATE TABLE `t_product` (
   `pid` int NOT NULL AUTO_INCREMENT,
   `productName` varchar(255) DEFAULT NULL,
   `price` double(10,2) DEFAULT NULL,
   `stock` int DEFAULT NULL,
   `description` varchar(255) DEFAULT NULL,
   `createtime` datetime DEFAULT NULL,
   `updatetime` datetime DEFAULT NULL,
   PRIMARY KEY (`pid`)
 ) ENGINE=InnoDB AUTO_INCREMENT=105 DEFAULT CHARSET=utf8;

创建Java项目,导入Spring和mybatis相关依赖

XML 复制代码
  <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <maven.compiler.source>17</maven.compiler.source>
     <maven.compiler.target>17</maven.compiler.target>
     <!-- 定义依赖的版本号   -->
     <spring.version>6.0.6</spring.version>
   </properties>
   <dependencies>
     <!--    junit测试依赖-->
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>3.8.1</version>
       <scope>test</scope>
     </dependency>
     <!-- spring相关依赖-->
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-beans</artifactId>
       <version>${spring.version}</version>
     </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-core</artifactId>
       <version>${spring.version}</version>
     </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context</artifactId>
       <version>${spring.version}</version>
     </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-expression</artifactId>
       <version>${spring.version}</version>
     </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-aop</artifactId>
       <version>${spring.version}</version>
     </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-aspects</artifactId>
       <version>${spring.version}</version>
     </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-jdbc</artifactId>
       <version>${spring.version}</version>
     </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-tx</artifactId>
       <version>${spring.version}</version>
     </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-orm</artifactId>
       <version>${spring.version}</version>
     </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-web</artifactId>
       <version>${spring.version}</version>
     </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-webmvc</artifactId>
       <version>${spring.version}</version>
     </dependency>
     <dependency>
       <groupId>org.projectlombok</groupId>
       <artifactId>lombok</artifactId>
       <version>1.18.30</version>
     </dependency>
     <!--    导入dom4j相关依赖-->
     <dependency>
       <groupId>org.dom4j</groupId>
       <artifactId>dom4j</artifactId>
       <version>2.2.0</version>
     </dependency>
     <dependency>
       <groupId>jakarta.annotation</groupId>
       <artifactId>jakarta.annotation-api</artifactId>
       <version>3.0.0</version>
     </dependency>
 <!--    导入mysql驱动包-->
     <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
       <version>8.0.30</version>
     </dependency>
 <!--    mybatis相关依赖-->
     <dependency>
       <groupId>org.mybatis</groupId>
       <artifactId>mybatis</artifactId>
       <version>3.5.19</version>
     </dependency>
 <!--    数据库连接池依赖  管理Connection对象-->
     <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>druid</artifactId>
       <version>1.2.12</version>
     </dependency>
 <!--    导入spring整合mybatis依赖-->
     <dependency>
       <groupId>org.mybatis</groupId>
       <artifactId>mybatis-spring</artifactId>
       <version>3.0.4</version>
     </dependency>
   </dependencies>

创建mybatis核心配置文件

XML 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE configuration
         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
         "https://mybatis.org/dtd/mybatis-3-config.dtd">
 <configuration>
 <!--    扫描指定包下xml映射文件-->
     <mappers>
        <package name="com.wn.mapper"/>
     </mappers>
 </configuration>

创建jdbc.properties配置文件

XML 复制代码
 db.driverClassName=com.mysql.cj.jdbc.Driver
 db.url=jdbc:mysql://localhost:3306/class118?serverTimezone=GMT
 db.username=root
 db.password=123456

创建实体类

java 复制代码
 /**
  * 商品类
  */
 @Data
 @AllArgsConstructor
 @NoArgsConstructor
 public class Product {
     private Integer pid;
     private String productName;
     private Double price;
     private Integer stock;
     private String description;
     private Date createtime;
     private Date updatetime;
 }

7、创建Mapper接口

复制代码
 
java 复制代码
/**
  * 商品 mapper层接口
  */
 public interface ProductMapper {
 ​
     /**
      * 查询所有商品信息
      */
     @Select("select * from t_product")
     List<Product> findAll();
 ​
 }

8、创建sevice层接口和实现类

ProductService接口:

java 复制代码
public interface ProductService {
 ​
     List<Product> findAll();
 }

ProductServiceImpl实现类:

java 复制代码
 /**
  * 商品业务层实现类
  */
 @Service
 public class ProductServiceImpl implements ProductService {
 ​
     @Autowired
     private ProductMapper productMapper;
     /**
      * 查询所有商品信息
      */
     @Override
     public List<Product> findAll() {
         return productMapper.findAll();
     }
 }

9、创建Controller层类

java 复制代码
 /**
  * 商品 控制层类
  */
 @Controller
 public class ProductController {
 ​
     @Autowired
     private ProductService productService;
 ​
 ​
     /**
      * 查询所有商品信息
      */
     public List<Product> findAll() {
         return productService.findAll();
     }
 }

10、创建Spring核心配置文件

复制代码
 
XML 复制代码
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop.xsd
     http://www.springframework.org/schema/tx
     http://www.springframework.org/schema/tx/spring-tx.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context.xsd
     http://www.springframework.org/schema/mvc
     http://www.springframework.org/schema/mvc/spring-mvc.xsd" >
 ​
     <!--    1.扫描指定包下及其子包下的spring注解-->
     <context:component-scan base-package="com.wn" />
 ​
     <!--    2.加载db.properties配置文件-->
     <context:property-placeholder location="classpath:mybatis/db.properties" />
 ​
     <!--   3.配置数据库连接池对象和参数 -->
     <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
         <!--        3.1、配置数据库连接池连接参数-->
         <property name="driverClassName" value="${db.driverClassName}"></property>
         <property name="url" value="${db.url}"></property>
         <property name="username" value="${db.username}"></property>
         <property name="password" value="${db.password}"></property>
     </bean>
 ​
     <!--    4.配置SqlSessionFactory对象和参数-->
     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
         <!--        4.1指定加载mybatis核心配置文件-->
         <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property>
         <!--        4。2、指定数据源-->
         <property name="dataSource" ref="dataSource"></property>
     </bean>
     <!--  5.扫描执行包下所有mapper接口,由spring容器管理代理对象 -->
     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
         <!--        5.1、执行扫描mapper接口包-->
         <property name="basePackage" value="com.wn.mapper"></property>
         <!--        指定sqlSessionfactory-->
         <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
     </bean>
 ​
 </beans>

11、测试类

java 复制代码
 public class UserControllerTest {
     public static void main(String[] args) throws SQLException {
         //1.加载spring核心配置文件
         ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
         //2.获取UserController对象
         UserController userController = (UserController) context.getBean("userController");
         //3.调用方法
 //        Map<String,Object> map = userController.queryByEmail("5664332222@qq.com");
 //        System.out.println(map);
         //调用转账方法
         userController.transactionAccount("张三", "李四", 1000);
     }
 }
相关推荐
绝顶少年1 小时前
Redis 高可用架构三部曲:主从复制、哨兵模式与集群模式深度解析
数据库·redis·架构
7哥♡ۣۖᝰꫛꫀꪝۣℋ1 小时前
Spring Boot ⽇志
java·spring boot·后端
清晓粼溪1 小时前
Mybatis02:核心功能
java·mybatis
weisonx1 小时前
为什么要多写文章博客
java·c++
倔强的石头1061 小时前
从 Oracle 到 KingbaseES:破解迁移痛点,解锁信创时代数据库新可能
数据库·oracle·金仓数据库
大佐不会说日语~1 小时前
SSE 流式输出 Markdown 实时渲染问题解决方案
java·vue.js·sse·spring ai·前端实时渲染
2301_800256111 小时前
8.3 查询优化 核心知识点总结
大数据·数据库·人工智能·sql·postgresql
塔能物联运维1 小时前
设备断网时数据丢失,后来启用本地缓存+异步重传队列
java·开发语言·缓存