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

news2024/10/22 3:32:12

前言

  • 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/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-3.0.xsd
	http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
     http://www.springframework.org/schema/tx
     http://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 mapper
        PUBLIC "-//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 {
    @Autowired
    private UserDao userDao;


    @Override
    public int tranAddUser(User user) {
        return userDao.tranAddUser(user);
    }

    @Override
    public int tranDelUser(int id) {
        return userDao.tranDelUser(id);
    }

    @Override
    public 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 {
    @Autowired
    private UserService userService;


    }
    @Test
    public void testTran(){
        User user =new User();
        user.setName("曹操");
        user.setAge(45);
        userService.tranAddUser(user);
        userService.tranDelUser(46);
    }

    @Test
    public void transTran(){
        int i = userService.transTest();
    }
}

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

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

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

抛出sql异常:

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

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

抛出sql异常:

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

总结

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


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

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

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

相关文章

CTFHUB技能树之文件上传——无验证

开启靶场&#xff0c;打开链接&#xff1a; 直接上传一句话木马&#xff1a; <?php eval($_POST[pass]);?> 成功提交并显示了上传的文件的路径 访问一下该文件触发一句话木马&#xff1a; 看到一片空白是正常的&#xff0c;因为没有写什么函数&#xff0c;比如&#x…

FineReport 计算同比增长

1、数据库查询 SELECTt1.年,t1.月,t1.总金额 AS 同期金额,t1.仓库名称,t2.总金额 AS 上期金额 FROMtest t1LEFT JOIN test t2 ON ( t1.年 t2.年 1 ) AND t1.月 t2.月 AND t1.仓库名称 t2.仓库名称2、配置字段 月份字段加后缀 月 数据列加后缀 计算同比增长率 if(LEN(B3)0 …

移动零---双指针法

目录 一&#xff1a;题目 二:算法原理讲解 三&#xff1a;代码编写 一&#xff1a;题目 题目链接&#xff1a;https://leetcode.cn/problems/move-zeroes/description/ 二:算法原理讲解 三&#xff1a;代码编写 void moveZeroes2(vector<int>& nums) {for (int d…

数据轻松上云——Mbox边缘计算网关

随着工业4.0时代的到来&#xff0c;工厂数字化转型已成为提升生产效率、优化资源配置、增强企业竞争力的关键。我们凭借其先进的边缘计算网关与云平台技术&#xff0c;为工厂提供了高效、稳定的数据采集与上云解决方案。本文将为您介绍Mbox边缘计算网关如何配合明达云平台&…

基于Java语言的培训平台+学习平台+在线学习培训系统+教育平台+教育学习系统+课程学习平台

简述 企业培训平台企业考试系统培训平台考试系统企业大学企业视频网站视频学习平台 介绍 企业培训平台支持企业培训考试全流程&#xff0c;在线学习、在线考试&#xff0c;成熟的企业培训考试解决方案&#xff0c;充分满足企业培训需求。 独立部署&#xff0c;仅内部员工登录…

JAVA高级--常用类(九)

JAVA高级–常用类 观看b站尚硅谷视频做的笔记 1、字符串相关的类 1.1 String 的使用 String 的特性&#xff1a; String 类&#xff1a;代表字符串。Java 程序中的所有字符串字面值&#xff08;如 “abc”&#xff09;都作为此类的实例实现。 String 是一个 final 类&#…

ESP8266 模块介绍—AT指令学习 笔记

零、简介 感谢百文网韦东山 老师对ESP8266模块的讲解 笔记在CSDN也有文章备份 大家可以在我的gitee仓库 中下载笔记源文件、ESP8266资料等 笔记源文件可以在Notion中导入 一、ESP8266-01S模块详细介绍 1. 名字的由来 ESP8266 是方形的主控芯片旁边的长方形是一个Flash-0…

IO编程——消息队列

题目&#xff1a; 代码实现&#xff1a; #include <myhead.h> //正文大小 #define MSGSZ (sizeof(struct msgbuf)-sizeof(long)) //定义要发送的消息类型 struct msgbuf{long msgtype; //消息类型char mtext[1024]; //消息正文 };int main(int argc, const char *ar…

全面升级:亚马逊测评环境方案的最新趋势与实践

在亚马逊测评领域深耕多年&#xff0c;见证了无数环境方案的更迭与演变&#xff0c;每一次变化都体现了国人不畏艰难、勇于创新的精神。面对平台的政策调整&#xff0c;总能找到相应的对策。那么&#xff0c;当前是否存在一套相对稳定且高效的技术方案呢&#xff1f;答案是肯定…

【计网】从零开始理解TCP协议 --- 拥塞控制机制,延迟应答机制,捎带应答,面向字节流

时间就是性命。 无端的空耗别人的时间&#xff0c; 其实是无异于谋财害命的。 --- 鲁迅 --- 从零开始理解TCP协议 1 拥塞控制2 延迟应答3 捎带应答4 面向字节流5 TCP异常情况TCP小结 1 拥塞控制 尽管TCP拥有滑动窗口这一高效的数据传输机制&#xff0c;能够确保在对方接收…

基于卷积神经网络的蔬菜识别系统,resnet50,mobilenet模型【pytorch框架+python源码】

更多目标检测和图像分类识别项目可看我主页其他文章 功能演示&#xff1a; 基于卷积神经网络的蔬菜识别系统&#xff0c;resnet50&#xff0c;mobilenet【pytorch框架&#xff0c;python&#xff0c;tkinter】_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于卷积神…

关于vue3中如何实现多个v-model的自定义组件

实现自定义组件<User v-model"userInfo" v-model:gender"gender"></User> User组件中更改数据可以同步更改父组件中的数据&#xff1a; 1 父组件&#xff1a; <User v-model"userInfo" v-model:gender"gender">&…

ROUGE:摘要自动评估软件包

算法解析 ROUGE&#xff08;Recall-Oriented Understudy for Gisting Evaluation&#xff09;是一组用于自动评估文本摘要质量的指标&#xff0c;主要通过比较机器生成的摘要与一个或多个参考摘要之间的重合程度来衡量。ROUGE 包括多个变体&#xff0c;其中最常用的有 ROUGE-N…

基于Java微信小程序的的儿童阅读系统的详细设计和实现(源码+lw+部署文档+讲解等)

详细视频演示 请联系我获取更详细的演示视频 项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不…

WebGoat SQL Injection (intro) 源码分析

首先了解 java 中 mysql 的连接&#xff1a;java连接Mysql WebGoat SQL Injection (intro) 10 根据提示&#xff1a;下面两个输入框只有一个受到 sql 注入攻击。题目要求是检索到所有数据 发现请求路径为 SqlInjection/assignment5b 定位到所在文件如下&#xff0c;根据代码…

基于递推式最小二乘法的PMSM参数辨识MATLAB仿真模型

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 模型简介 最小二乘法是一种回归估计法&#xff0c;适用于被辨识的参数与系统输出为线性关 系的情况。它是在一定数据量下&#xff0c;基于系统输出误差的平方和最小的准则对参 数进行辨识的方法。此模型通过…

使用Flask实现本机的模型部署

前言 模型部署是指将大模型运行在专属的计算资源上&#xff0c;使模型在独立的运行环境中高效、可靠地运行&#xff0c;并为业务应用提供推理服务。其目标是将机器学习模型应用于实际业务中&#xff0c;使最终用户或系统能够利用模型的输出&#xff0c;从而发挥其作用。 一、设…

unity 屏幕波动反馈打击效果(附资源下载)

unity 屏幕波动反馈打击效果 一枪打出去整个屏幕都回波动的效果反馈。 知识点&#xff1a; 1、动画事件 2、屏幕后处理 效果如图&#xff1a;&#xff08;波动速度浮动都可调整&#xff09; 附件下载

TH-OCR:高效的文字识别工具与护照阅读器的完美结合

在数字化的时代&#xff0c;高效准确的文字识别工具变得越来越重要。今天我要给大家介绍一下 TH-OCR。 TH-OCR 是一款功能强大的文字识别软件&#xff0c;它能够快速准确地识别各种文档中的文字&#xff0c;大大提高了工作效率。 而其中&#xff0c;TH-OCR 在护照阅读器方面的应…

Gitlab 完全卸载–亲测可行

1、停止gitlab gitlab-ctl stop2.卸载gitlab&#xff08;注意这里写的是gitlab-ce&#xff09; rpm -e gitlab-ce 3、查看gitlab进程 ps aux | grep gitlab 4、杀掉第一个进程&#xff08;就是带有好多.............的进程&#xff09; 5、删除所有包含gitlab文件 find / …