Spring声明式事务管理:深入探索XML配置方式

前言

  • Spring的事务管理,无论是基于xml还是注解实现,本质上还是实现数据库的事务管理机制,因此要注意发送SQL的连接是否为同一个,这是实现声明式事务的关键。
  •  以下案例和实现基于SSM整合框架完成,不知道如何整合SSM,可以参考我之前的博客。

准备工作

 第一步:添加Spring相关依赖包

  1. spring-context:提供了Spring框架的核心功能,包括依赖注入和控制反转等。事务管理是Spring框架的核心功能之一。

  2. spring-tx:该依赖提供了Spring事务管理的相关类和接口,用于定义事务的属性、管理事务的生命周期、处理事务的提交和回滚等。

  3. spring-jdbc:如果需要在Spring框架中使用JDBC(Java数据库连接)进行数据库操作,需要引入该依赖。在进行数据库操作时,可以通过事务管理来保证数据的一致性和完整性。

  4. spring-test:在进行事务管理的单元测试时,需要引入该依赖。它提供了一些测试用例的工具方法,可以方便地进行事务管理的测试。

第二步:准备相关数据库和数据库表

 t_user表如下

包含字段:id,name,password,age,代表了一个用户的基本信息

新增用户失败事务回滚案例实现

在SSM中spring的配置文件和mybatis的配置文件整合到了一起,所以事务管理器的相关配置写在spring的配置文件。

spring-mybtais.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:context="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-3.0.xsd"><!--开启扫描 --><context:component-scan base-package="com.csx"/><!--读取jdbc配置文件--><bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="location" value="classpath:mybatis/jdbc.properties"/></bean><!--配置数据源,可以替换成druid数据源--><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"><property name="driverClassName" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/><property name="initialSize" value="${initialSize}"/><property name="maxActive" value="${maxActive}"/><property name="maxIdle" value="${maxIdle}"/><property name="minIdle" value="${minIdle}"/><property name="maxWait" value="${maxWait}"/></bean><!--扫描mapper--><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="mapperLocations" value="classpath:mapper/*.xml"/><property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/><property name="typeAliasesPackage" value="com.csx.entity"/></bean><!--扫描dao--><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/><property name="basePackage" value="com.csx.dao"/></bean><!--事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!--	spring事务传播特性:事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。--><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="*" propagation="REQUIRED"/>
<!--            <tx:method name="tranAdd*" propagation="REQUIRED"/>-->
<!--            <tx:method name="tranDel*" propagation="REQUIRED"/>-->
<!--            <tx:method name="update*" propagation="REQUIRED"/>-->
<!--            <tx:method name="get*" propagation="SUPPORTS"/>-->
<!--            <tx:method name="query*" propagation="SUPPORTS"/>--></tx:attributes></tx:advice><aop:config><aop:pointcut id="txPointCut" expression="execution(* com.csx.service.*.*(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/></aop:config></beans>	

以上是spring整合mybatis的完整配置文件。接下来我会详细介绍spring声明式的xml配置以及其含义。

 配置数据源

<!--配置数据源,可以替换成druid数据源-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"><property name="driverClassName" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/><property name="initialSize" value="${initialSize}"/><property name="maxActive" value="${maxActive}"/><property name="maxIdle" value="${maxIdle}"/><property name="minIdle" value="${minIdle}"/><property name="maxWait" value="${maxWait}"/>
</bean>

配置数据源(数据库连接池),是非常重要的,目的是对我们的数据库连接集中管理,保证我们后续事务管理过程中,不同的sql使用同一条数据库连接。

定义事务管理器

<!--事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean>
  • 将事务管理器的id声明为transactionManager的目的是为了下面配置的时候少写一个属性,一会具体说明
  • 事务管理器的属性就是引用我们刚才定义的数据源

 配置事务管理器

   <!-- spring事务传播特性:事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。--><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="*" propagation="REQUIRED"/>
<!--            <tx:method name="tranAdd*" propagation="REQUIRED"/>-->
<!--            <tx:method name="tranDel*" propagation="REQUIRED"/>-->
<!--            <tx:method name="update*" propagation="REQUIRED"/>-->
<!--            <tx:method name="get*" propagation="SUPPORTS"/>-->
<!--            <tx:method name="query*" propagation="SUPPORTS"/>--></tx:attributes></tx:advice>
  • 主要是为了配置事务的传播行为(即,当多个事务集中管理时,怎么控制不同的事务的执行)
  • 这里的transaction-manager="transactionManager",如果我们刚才在上面定义事务管理器的id也是transactionMannger,则可以省略transaction-manager这个属性,底层自动配置对应的事务管理器,如果名称不一致,则必须配置这个属性

 配置事务的AOP

spring声明式事务,本质上底层还是基于spring的aop实现的,将多条sql统一使用同一条数据库连接管理事务

<aop:config><aop:pointcut id="txPointCut" expression="execution(* com.csx.service.*.*(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
  • pointcut,定义aop的切入点表达式,事务的控制一般是在service层进行的(如果多条sql的dao方法,不是在同一个service层方法存在,则事务失效,没有被集中管理(相当于没有书写事务))
  • advisor,配置切面和切入点的关系,定义事务管理管理的方法

 

 Dao层

UserDao

public interface UserDao {//声明式事务int tranAddUser(User user);int tranDelUser(int id);
}

 UserDao.xml

<?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为映射的根节点,用来管理DAO接口 namespace指定DAO接口的完整类名,表示mapper配置文件管理哪个DAO接口(包.接口名)mybatis会依据这个接口动态创建一个实现类去实现这个接口,而这个实现类是一个Mapper对象-->
<mapper namespace="com.csx.dao.UserDao"><!--声明式事务管理--><insert id="tranAddUser">insert into t_user(name ,age)values (#{name},#{age})</insert><delete id="tranDelUser">deletes  from t_user where  id=#{id}</delete></mapper>

这里故意将delete写成deletes,目的是让删除操作发生sql异常,以便观察sql是否发生回滚

Service层

 UserService

package com.csx.service;import com.csx.entity.User;
import com.github.pagehelper.Page;import java.util.Map;public interface UserService {/*事务错误演示*/int tranAddUser(User user);int tranDelUser(int id);//事务正确演示int transTest();
}
  • 将两个dao层的sql操作,写在同一个service层的方法内,可以正确管理事务;
  • 将两个dao层的sql操作,写在不同的service层的方法内,无法正确管理事务(不同的service层方法不属于同一个事务管理的范围)

UserServiceImpl

package com.csx.service.impl;import com.csx.dao.UserDao;
import com.csx.entity.User;
import com.csx.service.UserService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;
import java.util.Map;
@Service(value = "userService")
public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;@Overridepublic int tranAddUser(User user) {return userDao.tranAddUser(user);}@Overridepublic int tranDelUser(int id) {return userDao.tranDelUser(id);}@Overridepublic int transTest() {User user =new User();user.setName("曹操");user.setAge(45);userDao.insertUser(user);//删除操作一定会抛出sql异常userDao.tranDelUser(46);return 1;}
}

单元测试

import com.csx.entity.User;
import com.csx.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring/spring-mybatis.xml"})
public class UserTest {@Autowiredprivate UserService userService;}@Testpublic void testTran(){User user =new User();user.setName("曹操");user.setAge(45);userService.tranAddUser(user);userService.tranDelUser(46);}@Testpublic void transTran(){int i = userService.transTest();}
}

testTran():一定测试失败,本质上它们不属于同一个事务,因此互不影响。

transTran():事务生效,被事务管理控制

单元测试效果演示testTran(): 

抛出sql异常:

数据库数据依然新增成功,事务失效 

单元测试效果演示transTran():

抛出sql异常:

数据库数据没有新增,事务生效

总结

基于xml的spring声明式事务,需要注意以下几点:


  • 配置切入点表达式时,注意应当切入的是service层的方法,在业务层管理不同的dao的sql,从而达到事务控制的效果。
  • 将多条sql的操作,放在同一个service方法,事务才会生效,放在不同的service方法,相当于没有进行事务管理,还是一个dao的sql对应一个数据库连接。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/57316.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【K8S系列】Kubernetes Pod 状态详细介绍及异常状态解决方案

在 Kubernetes 中&#xff0c;Pod 是最小的可调度单元&#xff0c;负责运行一个或多个容器。Pod 的状态能够反映其生命周期中的不同阶段&#xff0c;帮助用户了解当前的运行状况。本文将详细介绍 Kubernetes Pod 的各种状态及其可能的异常状态解决方案。 一、Pod 状态概览 Po…

查缺补漏----数据结构树高总结

① 对于平衡二叉树而言&#xff0c;树高的规律&#xff1a; 高度为h的平衡二叉树的含有的最少结点数&#xff08;所有非叶节点的平衡因子均为1&#xff09;&#xff1a; n01&#xff0c;n11&#xff0c;n22 含有的最多结点数&#xff1a; (高度为h的满二叉树含有的结点数) ②…

Flutter在 iOS 中实现无弹窗获取剪切板内容

前言 在最新的项目需求中&#xff0c;我们需要在获取剪切板内容时避免弹出授权提示。这一功能是基于竞品的实现&#xff0c;旨在优化用户体验&#xff0c;特别是在推广获取跳转链接的场景下非常有用。 解决方案 通过查阅资料&#xff0c;我们发现对于 iOS 16 及以上的系统&a…

Fusion创建一个简单的api脚本文件

我的Fusion版本&#xff1a;Fusion 2.0.20476 x86_64 脚本模块在实用程序->附加模型->脚本和附加模块&#xff0c;快捷键为shifts 里面有一些演示脚本&#xff0c;可以直接使用 也可以自己创建一个新的脚本 创建的脚本在此处—— 选择脚本文件&#xff0c;点击编辑&a…

Unity Mirror NetworkManager初识

文章目录 Network Manager网络管理器什么是网络管理器&#xff1f;通过Transports进行定制化网络连接管理自定义连接地址和端口号Game State Management游戏状态管理Network Manager HUD玩家预制体及其生成控制Spawn Prefabs其他预制体注册Scene Management场景管理 Network Ma…

在Windows系统中,cmd 查看 MongoDB 相关信息

MongoDB是一种流行的NoSQL数据库&#xff0c;广泛应用于各种现代应用程序中。 1 查看MongoDB的版本号 要查看MongoDB的版本号&#xff0c;可以使用mongo命令连接到MongoDB&#xff0c;然后执行db.version()。 mongo连接到数据库后&#xff0c;执行以下命令&#xff0c;输出M…

读数据工程之道:设计和构建健壮的数据系统16源系统实际细节(下)

1. 数据共享 1.1. 云数据共享的核心概念是&#xff0c;多租户系统支持租户之间共享数据的安全策略 1.2. 任何具有细粒度权限系统的公有云对象存储系统都可以成为数据共享的平台 1.3. 数据共享也简化了数据市场的概念&#xff0c;在几个流行的云和数据平台上都可用 1.4. 数据…

RabbitMQ系列学习笔记(三)--工作队列模式

文章目录 一、工作队列模式原理二、工作队列模式实战1、抽取工具类2、消费者代码3、生产者代码4、查看运行结果 本文参考 尚硅谷RabbitMQ教程丨快速掌握MQ消息中间件rabbitmq RabbitMQ 详解 Centos7环境安装Erlang、RabbitMQ详细过程(配图) 一、工作队列模式原理 与简单模式相…

SpringBoot篇(二、制作SpringBoot程序)

目录 一、代码位置 二、四种方式 1. IDEA联网版 2. 官网 3. 阿里云 4. 手动 五、在IDEA中隐藏指定文件/文件夹 六、复制工程-快速操作 七、更改引导类别名 一、代码位置 二、四种方式 1. IDEA联网版 2. 官网 官网制作&#xff1a;Spring Boot 3. 阿里云 阿里云版制…

基于SSM+微信小程序的家庭记账本管理系统(家庭1)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 1、管理员端功能有首页、个人中心、用户管理&#xff0c;消费详情管理、收入详情管理、系统管理等。 2、用户端功能有首页、消费详情、收入详情、论坛信息、我的等功能。 2、项目技术 …

django5入门【02】创建新的django程序

注意&#xff1a; ⭐前提&#xff1a;已经安装了python以及django所依赖的包1、通过django-admin管理工具在命令行创建Django应用程序&#xff0c;创建命令如下&#xff1a; django-admin startproject ProjectName❓ 疑问&#xff1a;除了使用命令行创建django程序外&#x…

OCR经典神经网络(三)LayoutLM v2算法原理及其在发票数据集上的应用(NER及RE)

OCR经典神经网络(三)LayoutLM v2算法原理及其在发票数据集上的应用(NER及RE) LayoutLM系列模型是微软发布的、文档理解多模态基础模型领域最重要和有代表性的工作&#xff1a; LayoutLM v2&#xff1a;在一个单一的多模态框架中对文本&#xff08;text&#xff09;、布局&…

eQEP正交解码

目录 基本介绍 整体框架 关键模块 编译问题 实验效果 基本介绍 编码器是一种将角位移或者角速度转换成一连串电数字脉冲的旋转式传感器&#xff0c;我们可以通过编码器测量到位移或者速度信息。编码器从输出数据类型上分&#xff0c;可以分为增量式编码器和绝对式编码器。…

深入浅出MySQL:概述与体系结构解析

目录 1. 初识MySQL1.1. 数据库1.1.1. OLTP&#xff08;联机事务处理&#xff09;1.1.2. OLAP&#xff08;联机分析处理&#xff09; 2. SQL2.1. 定义2.2. DQL&#xff08;数据查询语言&#xff09;2.3. DML&#xff08;数据操纵语言&#xff09;2.4. DDL&#xff08;数据定义语…

Python基于OpenCV的实时疲劳检测

2.检测方法 1&#xff09;方法 与用于计算眨眼的传统图像处理方法不同&#xff0c;该方法通常涉及以下几种组合&#xff1a; 1、眼睛定位。 2、阈值找到眼睛的白色。 3、确定眼睛的“白色”区域是否消失了一段时间&#xff08;表示眨眼&#xff09;。 相反&#xff0c;眼睛长…

Python网络请求库requests的10个基本用法

大家好&#xff01;今天我们要聊聊Python中非常实用的一个库——requests。这个库让发送HTTP请求变得超级简单。无论你是想抓取网页数据还是测试API接口&#xff0c;requests都能派上大用场。下面我们就一起来看看如何使用requests完成一些常见的任务。 引言 随着互联网技术的…

队列(数据结构)——C语言

目录 1.概念与结构 2.队列的实现 初始化QueueInit 申请新节点BuyNode 入队QueuePush 判断队为空QueueEmpty 出队QueuePop 读取队头数据QueueFront 读取队尾数据QueueBack 元素个数QueueSize 销毁队列QueueDestroy 3.整体代码 (文章中结点和节点是同一个意思) 1.概…

keil兼容C51和ARM,C251

三合一 C51,AEM,C251获取STC32的包 将 C51,AEM,C251安装到一块。 C51,AEM,C251 将三个软件分别下载到不同的文件夹KEIL,MDK,KEIL2里。 然后打开KEIL,MDK,KEIL2文件夹&#xff0c;复制KEIL文件夹里的C51和KEIL2文件夹里的C251的文件夹到MDK文件夹里。 打开KEIL和KEIL2文件夹里…

单链表的经典算法OJ

目录 1.反转链表 2.链表的中间节点 3.移除链表元素 ——————————————————————————————————————————— 正文开始 1.反转链表 typedef struct ListNode ListNode; struct ListNode* reverseList(struct ListNode* head) {//判空if(…

运行kafka查看所有主题Topic报错zookeeper is not a recognized option

执行命令查看&#xff1a;./kafka-topics.sh --list --zookeeper localhost:2181 报错 zookeeper is not a recognized option joptsimple.UnrecognizedOptionException: zookeeper is not a recognized optionat joptsimple.OptionException.unrecognizedOption(OptionExcept…