15、Spring之基于xml的声明式事务
阅读本文前,建议先阅读Spring之基于注解的声明式事务
15.1、环境搭建
创建名为spring_transaction_xml的新module,过程参考13.1节
15.1.1、配置打包方式和依赖

注意:比起基于注解的声明式事务,基于xml的声明式事务还需要额外引入spring-AOP的依赖
<?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>org.rain</groupId>
<artifactId>spring_transaction_xml</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!-- Spring IOC相关 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<!-- Spring 持久化层相关 -->
<!-- Spring 在执行持久化层操作、与持久化层技术进行整合过程中,需要使用orm、jdbc、tx三个jar包 -->
<!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.1</version>
</dependency>
<!-- Spring 测试相关 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.1</version>
</dependency>
<!-- spring-AOP的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.1</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
</dependencies>
</project>
15.1.2、创建jdbc.properties文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
15.1.3、创建Spring的配置文件

<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 导入外部属性文件 -->
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>
<!-- 配置数据源 -->
<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
<!--通过${key}的方式访问外部属性文件的value-->
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置 JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 装配数据源 -->
<property name="dataSource" ref="datasource"></property>
</bean>
</beans>
15.1.4、创建持久层接口BookDao及其实现类

package org.rain.spring.dao;
/**
* @author liaojy
* @date 2023/9/3 - 17:34
*/
public interface BookDao {
/**
* 查询图书的价格
* @param bookId
* @return
*/
Integer getPriceByBookId(Integer bookId);
/**
* 更新图书的库存
* @param bookId
*/
void updateStockOfBook(Integer bookId);
/**
* 更新用户的余额
* @param userId
* @param price
*/
void updateBalanceOfUser(Integer userId,Integer price);
}

package org.rain.spring.dao.impl;
import org.rain.spring.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
/**
* @author liaojy
* @date 2023/9/3 - 17:36
*/
@Repository
public class BookDaoImpl implements BookDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public Integer getPriceByBookId(Integer bookId) {
String sql = "select price from t_book where book_id = ?";
Integer price = jdbcTemplate.queryForObject(sql, Integer.class,bookId);
return price;
}
public void updateStockOfBook(Integer bookId) {
String sql = "update t_book set stock = stock -1 where book_id = ?";
jdbcTemplate.update(sql, bookId);
}
public void updateBalanceOfUser(Integer userId, Integer price) {
String sql = "update t_user set balance = balance - ? where user_id = ?";
jdbcTemplate.update(sql,price,userId);
}
}
15.1.5、创建业务层接口BookService及其实现类

package org.rain.spring.service;
/**
* @author liaojy
* @date 2023/9/3 - 17:52
*/
public interface BookService {
void buyBook(Integer bookId,Integer userId);
}

package org.rain.spring.service.impl;
import org.rain.spring.dao.BookDao;
import org.rain.spring.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author liaojy
* @date 2023/9/3 - 17:53
*/
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
public void buyBook(Integer bookId, Integer userId) {
//查询图书的价格
Integer price = bookDao.getPriceByBookId(bookId);
//更新图书的库存
bookDao.updateStockOfBook(bookId);
//更新用户的余额
bookDao.updateBalanceOfUser(userId,price);
}
}
15.1.6、创建控制层BookController

注意:因为控制层没用到接口,所以方法的访问修饰符要手动设置
package org.rain.spring.controller;
import org.rain.spring.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
/**
* @author liaojy
* @date 2023/9/3 - 17:54
*/
@Controller
public class BookController {
@Autowired
private BookService bookService;
public void buyBook(Integer bookId, Integer userId){
bookService.buyBook(bookId,userId);
}
}
15.1.7、配置对注解组件的扫描

<!--扫描注解组件-->
<context:component-scan base-package="org.rain.spring"></context:component-scan>
15.2、基于xml事务的实现
15.2.1、配置事务管理器

<!--配置事务管理器-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 装配要进行事务管理的数据源 -->
<property name="dataSource" ref="datasource"></property>
</bean>
15.2.2、配置事务通知

注意:tx:advice标签导入的名称空间需要 tx 结尾的那个
<!--
tx:advice标签:配置事务通知
id属性:给事务通知标签设置唯一标识,便于引用
transaction-manager属性:通过id引用对应的事务管理器
-->
<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager"></tx:advice>
15.2.3、配置事务通知作用到连接点

<aop:config>
<!--
通过切入点表达式,将事务通知作用到连接点
-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* org.rain.spring.service.impl.*.*(..))"></aop:advisor>
</aop:config>
15.2.4、配置事务通知的属性

注意:不使用tx:method标签指定的方法是不会使用事务的,为了让切入点表达式的所有方法都使用到事务,可以使用*通配符:
<tx:method name="*"/>
<tx:attributes>
<!--
tx:method标签:配置使用事务的方法
name属性:指定用事务的方法名,可以使用星号代表多个字符
-->
<tx:method name="buyBook"/>
</tx:attributes>
15.3、测试事务的效果
15.3.1、创建测试类
模拟场景:
用户购买图书,先查询图书的价格,再更新图书的库存和用户的余额
假设id为1的用户(余额为50),购买id为1的图书(价格为80)
购买图书之后,用户的余额应为-30;但由于数据库中余额字段设置了无符号,因此无法将-30插入到余额字段;
此时执行更新用户余额的sql语句会抛出异常

package org.rain.spring.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.rain.spring.controller.BookController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author liaojy
* @date 2023/9/4 - 0:20
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-tx-xml.xml")
public class TxByXml {
@Autowired
private BookController bookController;
@Test
public void testBuyBook(){
bookController.buyBook(1,1);
}
}
15.3.2、执行前的数据

此时id为1的图书库存为100

此时id为1的用户余额为50
15.3.3、执行时的异常

15.3.3、执行后的数据

由于使用了Spring的声明式事务,更新(图书)库存和更新(用户)余额,要么都成功,要么都失败;
本例属于都失败,所以(图书)库存和(用户)余额都没有变化

15.4、配置事务的属性

事务属性的详细作用,请参考14.5节
<!-- rollback-for属性:设置回滚的异常 -->
<!-- no-rollback-for属性:设置不回滚的异常 -->
<!-- isolation属性:设置事务的隔离级别 -->
<!-- timeout属性:设置事务的超时属性 -->
<!-- propagation属性:设置事务的传播行为 -->
<tx:method name="save*" read-only="false" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"/>
<tx:method name="update*" read-only="false" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"/>
<tx:method name="delete*" read-only="false" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"/>
15、Spring之基于xml的声明式事务的更多相关文章
- Spring中基于XML的声明式事务控制配置步骤
1.配置事务管理器 2.配置事务的通知 此时,我们就需要导入事务的约束 tx名称空间和约束,同时也需要aop的 使用tx:advice标签配置事务通知 属性: id:给事务通知起一个唯一标识 tran ...
- spring基于XML的声明式事务控制
<?xml version="1.0" encoding="utf-8" ?><beans xmlns="http://www.sp ...
- spring基于xml的声明式事务控制配置步骤
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- 阶段3 2.Spring_10.Spring中事务控制_6 spring基于XML的声明式事务控制-配置步骤
环境搭建 新建工程 把对应的依赖复制过来 src下内容复制 配置spring中的声明事物 找到bean.xml开始配置 配置事物管理器 里面需要注入DataSource 2-配置事物通知 需要先导入事 ...
- 基于XML的声明式事务控制
1.maven依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="h ...
- spring基于注解的声明式事务控制
package com.hope.service.impl;import com.hope.dao.IAccountDao;import com.hope.domain.Account;import ...
- Spring:(三) --常见数据源及声明式事务配置
Spring自带了一组数据访问框架,集成了多种数据访问技术.无论我们是直接通过 JDBC 还是像Hibernate或Mybatis那样的框架实现数据持久化,Spring都可以为我们消除持久化代码中那些 ...
- 使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用之前决定是否开启一个事务,并在方法执行之后决定事务提交或回滚事务。
使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用之前决定是否开启一个事务,并在方法执行之后决定事务提交或回滚事务.
- Spring注解驱动开发之声明式事务
前言:现今SpringBoot.SpringCloud技术非常火热,作为Spring之上的框架,他们大量使用到了Spring的一些底层注解.原理,比如@Conditional.@Import.@Ena ...
- spring 事物(三)—— 声明式事务管理详解
spring的事务处理分为两种: 1.编程式事务:在程序中控制事务开始,执行和提交:详情请点此跳转: 2.声明式事务:在Spring配置文件中对事务进行配置,无须在程序中写代码:(建议使用) 我对&q ...
随机推荐
- Qt开发技术:Q3D图表开发笔记(四):Q3DSurface三维曲面图颜色样式详解、Demo以及代码详解
前言 qt提供了q3d进行三维开发,虽然这个框架没有得到大量运用也不是那么成功,性能上也有很大的欠缺,但是普通的点到为止的应用展示还是可以的. 其中就包括华丽绚烂的三维图表,数据量不大的时候是可 ...
- 鸿蒙HarmonyOS实战-ArkTS语言基础类库(并发)
一.并发 并发是指在一个时间段内,多个事件.任务或操作同时进行或者交替进行的方式.在计算机科学中,特指多个任务或程序同时执行的能力.并发可以提升系统的吞吐量.响应速度和资源利用率,并能更好地处理多用户 ...
- kettle从入门到精通 第五十八课 ETL之kettle HTTP post使用教程
1.今天群里有位朋友问我有没有关于调用http接口的kettle 示例,我下意识的去翻我的公众号推文,愣是没找到.果断开始撸. 2.本次演示流程通过调用接口[网易云音乐随机歌曲],然后解析返回的数据, ...
- springboot和springmvc区别:
spring boot只是一个配置工具,整合工具,辅助工具.springmvc是框架,项目中实际运行的代码Spring 框架就像一个家族,有众多衍生产品例如 boot.security.jpa等等.但 ...
- @Transactional事务注解及请求接口的定义先后执行顺序设计
@Transactional事务注解及请求接口的定义先后执行顺序设计1.事务内查询,可能存在事务没有提交,导致查询数据查不出来. 2.或者可能跟请求参数作为查询条件,在某个条件下,请求参数发生变化,也 ...
- 在Winform程序中动态绘制系统名称,代替图片硬编码名称
在以前我做程序的时候,一般在登录窗口里面显示程序名称,登录窗口一般设置一张背景图片,由于程序的名称一般都是确定的,所以也不存在太大的问题,不过如果客户定制不同的系统的时候,需要使用Photoshop修 ...
- yum update和yum upgrade的区别
看见网上很多关于这个问题的解答,但是大部分都是错的,误人子弟! 很多都是执行这两个命令,然后查看系统的变化.看似严谨,实则愚蠢至极. 就算不懂内核,也应该懂得什么是内核呀!也应该懂得内核是怎么进入的啊 ...
- k8s实战 ---- pod 基础
如果你对k8s还不了解,可以看下前文 k8s 实战 1 ---- 初识 (https://www.cnblogs.com/jilodream/p/18245222) 什么是pod,pod在 ...
- Nginx负载配置
目录 Nginx 负载均衡笔记 1. 概述 1.1 Nginx 简介 1.2 负载均衡概述 2. 四层负载均衡(传输层) 2.1 工作原理 2.2 特点 2.3 优缺点 优点 缺点 2.4 示例场景 ...
- 重磅来袭!MoneyPrinterPlus一键发布短视频到视频号,抖音,快手,小红书上线了
MoneyPrinterPlus开源有一段时间了,已经实现了批量短视频混剪,一键生成短视频等功能. 有些小伙伴说了,我批量生成的短视频能不能一键上传到视频号,抖音,快手,小红书这些视频平台呢?答案是必 ...