Spring-事务2

news/2024/11/1 22:36:28/

文章目录

  • 前言
  • 一、事务的特性(ACID)
  • 二、事务的隔离级别
  • 三、spring中的事务
      • 平台事务管理器.
      • 事务定义
        • ISOLation_XXX:**事务隔离级别.**
        • PROPAGATION_XXX:**事务的传播行为**.
      • 事务状态
    • 关系:
  • 四、使用XML文件配置事务
    • 1、 搭建环境
    • 2、 配置及业务编写
    • 3、 测试
  • 五、使用注解形式进行事务开发
  • 总结


前言

事务是逻辑上的一组操作的集合,要么全部成功,要么全部失败。在实际的业务场景中,事务的应用场景范围很广泛,例如转账,订单,支付等


一、事务的特性(ACID)

事务有四大特性:

  • 原子性:事务不可分割
  • 一致性:事务执行的前后,数据完整性保持一致.
  • 隔离性:一个事务执行的时候,不应该受到其他事务的打扰
  • 持久性:一旦结束,数据就永久的保存到数据库.

二、事务的隔离级别

在实际的开发中,可能会出现以下几种由数据操作引起的异常情况:
如果不考虑隔离性:

  • 脏读:一个事务读到另一个事务未提交数据
  • 不可重复读:一个事务读到另一个事务已经提交数据(update)导致一个事务多次查询结果不一致
  • 虚读:一个事务读到另一个事务已经提交数据(insert)导致一个事务多次查询结果不一致

事务存在几种隔离级别:

  • 未提交读:以上情况都有可能发生。
  • 已提交读:避免脏读,但不可重复读,虚读是有可能发生。
  • 可重复读:避免脏读,不可重复读,但是虚读有可能发生。
  • 串行化:避免以上所有情况.

三、spring中的事务

后端分层开发中,事务应用于Service层中。spring提供了事务管理API

平台事务管理器.

PlatformTransactionManager

  • getTransaction(TransactionDefinition definition) :开启事务
  • rollback(TransactionStatus status) :回滚事务
  • commit(TransactionStatus status) :提交事务

Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现

使用Spring JDBC或iBatis 进行持久化数据时使用(重点)

  • org.springframework.jdbc.datasource.DataSourceTransactionManager

使用Hibernate进行持久化数据时使用

  • org.springframework.orm.hibernate.HibernateTransactionManager

使用JPA进行持久化时使用

  • org.springframework.orm.jpa.JpaTransactionManager

当持久化机制是Jdo时使用

  • org.springframework.jdo.JdoTransactionManager

使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用

  • org.springframework.transaction.jta.JtaTransactionManager

事务定义

TransactionDefinition

ISOLation_XXX:事务隔离级别.

ISOLATION_DEFAULT:默认级别. Mysql --> repeatable_read | Oracle -->> read_commited
级别如下:

  • ISOLATION_READ_UNCOMMITTED : 读未提交
  • ISOLATION_READ_COMMITTED : 读已提交
  • ISOLATION_REPEATABLE_READ :可重复读
  • ISOLATION_SERIALIZABLE :串行化

PROPAGATION_XXX:事务的传播行为.

事务的传播行为是用来解决实际开发中的问题
传播行为是指:解决业务层调用事务的关系。

  • PROPAGATION_REQUIRED: 支持当前事务,如果不存在 就新建一个

A,B 如果A有事务,B使用A的事务,如果A没有事务,B就开启一个新的事务.(A,B是在一个事务中。)

  • PROPAGATION_SUPPORTS: 支持当前事务,如果不存在,就不使用事务

A,B 如果A有事务,B使用A的事务,如果A没有事务,B就不使用事务.

  • PROPAGATION_MANDATORY: 支持当前事务,如果不存在,抛出异常

A,B 如果A有事务,B使用A的事务,如果A没有事务,抛出异常.

  • PROPAGATION_REQUIRES_NEW: 如果有事务存在,挂起当前事务,创建一个新的事务

A,B 如果A有事务,B将A的事务挂起,重新创建一个新的事务.(A,B不在一个事务中.事务互不影响.)

  • PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果有事务存在,挂起当前事务

A,B 非事务的方式运行,A有事务,就会挂起当前的事务.

  • PROPAGATION_NEVER: 以非事务方式运行,如果有事务存在,抛出异常

  • PROPAGATION_NESTED: 如果当前事务存在,则嵌套事务执行

基于SavePoint技术.
A,B A有事务,A执行之后,将A事务执行之后的内容保存到SavePoint.B事务有异常的话,用户需要自己设置事务提交还是回滚.

  • 常用:(重点)
    PROPAGATION_REQUIRED
    PROPAGATION_REQUIRES_NEW
    PROPAGATION_NESTED

事务状态

TransactionStatus

  • 是否有保存点

  • 是否是一个新的事务

  • 事务是否已经提交

关系:

关系:PlatformTransactionManager通过TransactionDefinition设置事务相关信息管理事务,管理事务过程中,产生一些事务状态,状态由TransactionStatus记录。

四、使用XML文件配置事务

spring支持声明式事务管理,只需要配置文件,不需要额外的编写代码
演示如下:

1、 搭建环境

创建account表并插入两条数据

CREATE TABLE `account` (`id` int(11) NOT NULL AUTO_INCREMENT,`money` double DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;INSERT INTO `account` VALUES ('1',  '1000');
INSERT INTO `account` VALUES ('2',  '1000');

新建项目略
导入pom.xml依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.wmj</groupId><artifactId>SpringTX</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><!-- spring相关依赖,版本必须保持一致 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.24</version></dependency><!--spring数据连接支持 --><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.24</version></dependency><!--aop支持 --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.9.1</version></dependency><!-- 事务 --><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.3.24</version></dependency><!-- mybatis相关依赖 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.11</version></dependency><!-- myabtis整合spring依赖,必须导入 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.7</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.31</version></dependency><!--德鲁伊数据连接池支持 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.9</version></dependency><!--lombok注解支持 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></dependency><!--log4j日志 --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><!--单元测试 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency></dependencies>
</project>

2、 配置及业务编写

配置mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 配置日志 --><settings><setting name="logImpl" value="LOG4J"/></settings></configuration>

配置日志

log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

配置数据源

db.username = 帐号
db.password = 密码
db.url = jdbc:mysql:///库名?serverTimezone=Asia/Shanghai&characterEncoding=UTF8
db.driverClassName = com.mysql.cj.jdbc.Driver

搭建后端业务代码

控制层

package com.wmj.controller;import com.wmj.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;/*** --效率,是成功的核心关键--** @Author lzl* @Date 2023/2/13 16:04*/
@Controller
public class TestController {@Autowiredprivate TestService service;public void testTx(Integer id1,Integer id2,Double money){service.transferMoney(id1,id2,money);}
}

业务层接口及其实现类

package com.wmj.service;/*** --效率,是成功的核心关键--** @Author lzl* @Date 2023/2/13 16:10*/public interface TestService {void transferMoney(Integer id1, Integer id2, Double money);
}
package com.wmj.service.impl;import com.wmj.mapper.TestMapper;
import com.wmj.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** --效率,是成功的核心关键--** @Author lzl* @Date 2023/2/13 16:11*/
@Service
public class TestServiceImpl implements TestService {@Autowiredprivate TestMapper mapper;@Overridepublic void transferMoney(Integer id1, Integer id2, Double money) {mapper.dmoney(id1,money);int a =  1/0;mapper.amoney(id2,money);}
}

持久层接口及其xml数据访问

package com.wmj.mapper;import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;/*** --效率,是成功的核心关键--** @Author lzl* @Date 2023/2/13 16:12*/
@Repository
public interface TestMapper {Integer dmoney(@Param("id1") Integer id1, @Param("money") Double money);Integer amoney(@Param("id2") Integer id2, @Param("money") Double money);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.wmj.mapper.TestMapper"><update id="dmoney">update account set money = money - #{money} where id = #{id1}</update><update id="amoney">update account set money = money + #{money} where id = #{id2}</update>
</mapper>

配置spring核心配置文件application.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:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- bean definitions here --><!-- 扫描包下的注解 --><context:component-scan base-package="com.wmj"></context:component-scan><!-- 引入db.properties配置文件 --><context:property-placeholder location="classpath:db.properties"></context:property-placeholder><!-- 配置数据库链接 --><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="username" value="${db.username}"></property><property name="password" value="${db.password}"></property><property name="url" value="${db.url}"></property><property name="driverClassName" value="${db.driverClassName}"></property></bean><!-- 配置SqlSession --><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><!-- dataSource属性(必选属性) --><property name="dataSource" ref="dataSource"></property><!-- configLocation属性配置mybatis-config.xml(非必选属性)--><property name="configLocation" value="classpath:mybatis-config.xml"></property><!-- mapperLocations属性用于配置Mapper.xml文件所在位置 (非必选属性)--><property name="mapperLocations" value="classpath:mapper/*.xml"></property></bean><!-- 扫描Mapper --><bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.wmj.mapper"></property></bean><!-- 事务核心管理器,封装了所有事务操作. 依赖于连接池 --><bean name="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"></property></bean><!-- 配置事务通知 --><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><!-- 企业中配置CRUD方法一般使用方法名+通配符*的形式配置通知,此时类中的方法名要和配置的方法名一致 --><!-- 以方法为单位,指定方法应用什么事务属性isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。--><tx:method name="transferMoney" isolation="REPEATABLE_READ"propagation="REQUIRED" read-only="false" /></tx:attributes></tx:advice><!-- 配置织入 --><aop:config><!-- 配置切点表达式 --><aop:pointcut expression="execution(* com.wmj.service.impl.*ServiceImpl.*(..))"id="txPc" /><!-- 配置切面 : 通知+切点 advice-ref:通知的名称 pointcut-ref:切点的名称 --><aop:advisor advice-ref="txAdvice" pointcut-ref="txPc" /></aop:config></beans>

3、 测试

package com.test;import com.wmj.controller.TestController;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** --效率,是成功的核心关键--** @Author lzl* @Date 2023/2/13 16:02*/public class Demo1 {@Testpublic void testTX(){ClassPathXmlApplicationContext applicationContext =new ClassPathXmlApplicationContext("application.xml");TestController controller = (TestController)applicationContext.getBean("testController");controller.testTx(1,2,200.00);}
}

执行结果:
当没有遇到异常时,转账能正常执行,不在演示,此处演示当遇到异常时的事务效果
代码执行前数据库:
在这里插入图片描述

在这里插入图片描述
当遇到异常,代码终止执行,此时数据库

在这里插入图片描述
转账并没有进行提交,此时说明我们的事务配置成功!

五、使用注解形式进行事务开发

spring给我们提供了事务注解开发的方式,大大减少了代码编写,十分方便

创建项目配置基本与上方相同,我们将application.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:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --><!-- 导入外部配置文件 db.properties --><context:property-placeholder location="classpath:db.properties"></context:property-placeholder><!-- 配置数据源对象 --><bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource"><!-- 导入 db.properties 中的值--><property name="username" value="${db.username}"></property><property name="password" value="${db.password}"></property><property name="url" value="${db.url}"></property><property name="driverClassName" value="${db.driver}"></property></bean><!-- 扫描对应包下的注解 --><context:component-scan base-package="com.wmj"></context:component-scan><!-- 配置sqlSessionFactory --><bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"><!-- 必选配置 --><property name="dataSource" ref="datasource"></property><!-- 非必选属性,根据自己需求去配置 --><!-- 导入 mybatis-config.xml --><property name="configLocation" value="classpath:mybatis-config.xml"></property><!-- 导入 Mapper.xml 文件,classpath后面不能有空格 --><property name="mapperLocations" value="classpath:mapper/*.xml"></property></bean><!-- 扫描 Mapper 接口,生成代理对象 --><bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!-- 指定扫描的具体位置 --><property name="basePackage" value="com.wmj.mapper"></property></bean><!-- 配置事务 --><!-- 事务平台管理器,封装了所有的事务操作,依赖数据源 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="datasource"></property></bean><!-- 开启注解驱动事务支持 --><tx:annotation-driven></tx:annotation-driven></beans>

将业务实现类改造如下:

package com.wmj.service.impl;import com.wmj.mapper.AccountMapper;
import com.wmj.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;/*** @Transactional 配置事务* 可以写在类上,也可以写在方法上*/
//@Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly = false)
@Service
package com.wmj.service.impl;import com.wmj.mapper.TestMapper;
import com.wmj.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** --效率,是成功的核心关键--** @Author lzl* @Date 2023/2/13 16:11*/
/*** @Transactional 配置事务* 可以写在类上,也可以写在方法上*/
//@Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly = false)
@Service
public class TestServiceImpl implements TestService {@Autowiredprivate TestMapper mapper;@Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly = false)@Overridepublic void transferMoney(Integer id1, Integer id2, Double money) {mapper.dmoney(id1,money);int a =  1/0;mapper.amoney(id2,money);}
}

测试略


总结

本次主要记录了spring的事务支持,及其两种开发方式。再会!!!


http://www.ppmy.cn/news/24599.html

相关文章

【LeetCode】每日一题(3)

目录 题目&#xff1a;1234. 替换子串得到平衡字符串 - 力扣&#xff08;Leetcode&#xff09; 题目的接口&#xff1a; 解题思路&#xff1a; 代码&#xff1a; 过啦&#xff01;&#xff01;&#xff01; 写在最后&#xff1a; 题目&#xff1a;1234. 替换子串得到平衡…

ROS2+nav2+激光雷达导航实践(上)

目录写在前面安装nav2nav2介绍nav2实践map地图保存地图读取状态估计(TF变换)所需TF坐标base_link -> sensor framesodom -> base_linkLaser Scan Matcher for ROS2map -> odomAMCL使用AMCL参数配置文件amcl_config.yaml效果过程中的知识点一、launch文件编写格式及方法…

进大厂必备的Java面试八股文大全(2023最新精简易懂版,八股文中的八股文)

为什么同样是跳槽&#xff0c;有些人薪资能翻三倍&#xff1f;” 最近一个粉丝发出了灵魂拷问&#xff0c;类似的问题我收到过很多次&#xff0c;身边也确实有认识的同事、朋友们有非常成功的跳槽经历和收益&#xff0c;先说一个典型例子&#xff1a; 学弟小 A 工作一年半&am…

关于尚硅谷Hadoop-报错解决方案日志

以后都会将学习Hadoop中遇到的问题写到这里&#xff0c;供自己参考&#xff0c;能帮到大家更好SecondaryNameNode未启动解决办法&#xff1a;可能是端口被占用&#xff08;我没遇到&#xff09;hadoop104未在/etc/hosts配置映射路径我在hadoop104的/etc/hosts 添加了所有hadoop…

leetcode470 用Rand7()实现Rand10()

力扣470 第一步&#xff1a;根据Rand7()函数制作一个可以随机等概率生成0和1的函数rand_0and1 调用Rand7()函数&#xff0c;随机等概率生成1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;6&#xff0c;7 这时我们设置&#xff1a;生成1&#xff0c;2&a…

Dubbo面试题2023

1、为什么要用Dubbo 随着服务化的进一步发展&#xff0c;服务越来越多&#xff0c;服务之间的调用和依赖关系也越来越复杂&#xff0c;诞生了面向服务 的架构体系(SOA)&#xff0c;也因此衍生出了一系列相应的技术&#xff0c;如对服务提供、服务调用、连接处理、通信协议、 …

如何写好 Python 的 Lambda 函数?

当你需要完成一件小工作时&#xff0c;在本地环境中使用这个函数&#xff0c;可以让工作如此得心应手&#xff0c;它就是 Lambda 函数。 Lambda 函数是 Python 中的匿名函数。有些人将它们简称为lambdas&#xff0c;它们的语法如下&#xff1a; lambda arguments: expression…

【吉先生的Java全栈之路】

吉士先生Java全栈学习路线&#x1f9e1;第一阶段Java基础: 在第一阶段:我们要认真听讲,因为基础很重要!基础很重要!基础很重要!!! 重要的事情说三遍。在这里我们先学JavaSE路线&#xff1b;学完之后我们要去学第一个可视化组件编程《GUI》&#xff1b;然后写个《贪吃蛇》游戏耍…