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

embedded/2024/10/25 12:06:06/

前言

  • 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

java">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

java">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

java">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;}
}

单元测试

java">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异常:

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

总结

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


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

http://www.ppmy.cn/embedded/132326.html

相关文章

基于SSM汽车零部件加工系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;员工管理&#xff0c;经理管理&#xff0c;零件材料管理&#xff0c;产品类型管理&#xff0c;产品信息管理&#xff0c;产品出库管理&#xff0c;产品入库管理 员工账号功能包括&#xff1a;系统首页…

活动目录域安装学习笔记

工作组 没有办法统一身份验证 没有办法统一管理 域 统一身份验证 统一管理域中的计算机和用户 域中的计算机通过DNS定位域控制器 一、安装活动目录 1.1设置域控制器&#xff08;DC&#xff09;IP地址为固定IP地址 ①打开命令提示符&#xff0c;或者通过快捷命令&#xff0c;…

51单片机快速入门之 步进电机的使用与 ULN2003 2024/10/21

51单片机快速入门之 步进电机的使用与 ULN2003 步进电机相关百科http://步进电机_360百科 https://baike.so.com/doc/656679-695086.html ULN2003相关百科 http://ULN2003_360百科 https://baike.so.com/doc/5356466-5591963.html ULN2003(图片来源于网络): 引脚1:CPU脉冲…

从安灯系统看汽车零部件工厂的智能制造转型

在当今快速发展的制造业领域&#xff0c;汽车零部件工厂正面临着日益激烈的市场竞争和不断提高的客户需求。为了在竞争中脱颖而出&#xff0c;实现可持续发展&#xff0c;许多汽车零部件工厂纷纷踏上智能制造转型之路。而安灯系统作为一种重要的生产管理工具&#xff0c;在这场…

影楼即将倒闭!!!!stable diffusion comfyui制作:AI人像摄影专业工作流

最近我们在学习ComfyUI&#xff0c;并用它搭建的摄影写真工作流&#xff0c;只需几张照片即可生成可交付的艺术写真照。 AI写真有以下好处&#xff1a; 创意无限&#xff1a;AI写真可以创造出超越现实的场景和效果&#xff0c;为用户提供更多的创意空间。用户可以通过简单的输…

MySQL 9从入门到性能优化-权限表

【图书推荐】《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;》-CSDN博客 《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;&#xff08;数据库技术丛书&#xff09;》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…

杨辉三角 II

给定一个非负索引 rowIndex&#xff0c;返回「杨辉三角」的第 rowIndex 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: rowIndex 3 输出: [1,3,3,1]示例 2: 输入: rowIndex 0 输出: [1]示例 3: 输入: rowIndex 1 输出: [1,1]提示…

开源模型应用落地-Qwen2-VL-7B-Instruct-vLLM-OpenAI API Client调用

一、前言 学习Qwen2-VL &#xff0c;为我们打开了一扇通往先进人工智能技术的大门。让我们能够深入了解当今最前沿的视觉语言模型的工作原理和强大能力。这不仅拓宽了我们的知识视野&#xff0c;更让我们站在科技发展的潮头&#xff0c;紧跟时代的步伐。 Qwen2-VL 具有卓越的图…