Spring篇(事务篇 - 基础介绍)
目录
一、JdbcTemplate(持久化技术)
1. 简介
2. 准备工作
2.1. 引入依赖坐标
2.2. 创建jdbc.properties
2.3. 配置Spring的配置文件
3. 测试
3.1. 在测试类装配 JdbcTemplate
3.2. 测试增删改功能
查询一条数据为实体类对象
查询多条数据为一个list集合
查询单行单列的值
二、SpringTransactional(事务管理)
1. 事务类型
1.1. 编程式事务
缺陷
1.2. 声明式事务
好处
1.3. 总结
2. 基于注解的声明式事务
2.1. 准备工作
加入依赖
创建jdbc.properties
配置Spring的配置文件
创建表
创建组件
2.2. 测试无事务情况
创建测试类
模拟场景
观察结果
2.3. 加入事务
添加事务配置
添加事务注解
观察结果
2.4. @Transactional注解标识的位置
2.5. 事务属性
1、只读:readOnly
介绍
使用方式
注意
2、超时:timeout
介绍
使用方式
观察结果
3、回滚策略:四种
介绍
使用方式
观察结果
4、事务隔离级别:Isolation
介绍
四种隔离级别
使用方式
5、事务传播行为:propagation
介绍
测试
观察结果
6、总结
3. 基于XML的Transactional
3.1. 场景模拟
3.2. 修改 Spring 配置文件
一、JdbcTemplate(持久化技术)
1. 简介
Spring 框架对 JDBC 进行了封装,使用 JdbcTemplate 可以让我们非常方便的实现对数据库的操作。
2. 准备工作
在使用JdbcTemplate之前,我们需要进行一些依赖配置,加入mysql-connector-java数据库连接驱动,
配置druid连接池技术,引入spring-orm包,在执行持久化层操作,与持久化层技术进行整合过程中,
需要使用orm、jdbc、tx三个jar包,导入·orm 包就可以通过 Maven 的依赖传递性把其他两个也jar包一起导入
2.1. 引入依赖坐标
<!-- Spring 持久化层支持jar包 --><!-- Spring 在执行持久化层操作、与持久化层技术进行整合过程中,需要使用orm、jdbc、tx三个jar包 --><!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 --><dependency><groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>5.3.1</version></dependency>
整体:
<dependencies><!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.1</version></dependency><!-- Spring 持久化层支持jar包 --><!-- 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><!-- 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>8.0.16</version></dependency><!-- 数据源 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.0.31</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.1</version></dependency></dependencies>
2.2. 创建jdbc.properties
有数据源环境,必定需要配置数据源文件:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.username=root
jdbc.password=root
2.3. 配置Spring的配置文件
再通过配置Spring的配置文件引入数据源:
<!--引入jdbc.properties--><context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><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><bean class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean>
3. 测试
3.1. 在测试类装配 JdbcTemplate
环境搭建完毕:开始测试环境是否搭建成功
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-jdbc.xml")
public class JdbcTemplateTest {@Autowiredprivate JdbcTemplate jdbcTemplate;}
3.2. 测试增删改功能
环境搭建成功后:开始测试 JdbcTemplate 的增删改功能
@Test
//测试增删改功能
public void testUpdate(){ String sql = "insert into t_emp values(null,?,?,?)"; int result = jdbcTemplate.update(sql, "张三", 23, "男"); System.out.println(result);
}
增删改测试完毕:开始测试 JdbcTemplate 的查询功能
查询功能主要有查询一条数据为实体类对象,查询多条数据为一个list集合,查询单行单列的值
查询一条数据为实体类对象
@Test
//查询一条数据为一个实体类对象
public void testSelectEmpById(){ String sql = "select * from t_emp where id = ?"; Emp emp = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Emp.class), 1); System.out.println(emp);
}
查询多条数据为一个list集合
@Test
//查询多条数据为一个list集合
public void testSelectList(){ String sql = "select * from t_emp"; List<Emp> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Emp.class)); list.forEach(emp -> System.out.println(emp));
}
查询单行单列的值
1、测试操作
@Test
//查询单行单列的值
public void selectCount(){ String sql = "select count(id) from t_emp"; Integer count = jdbcTemplate.queryForObject(sql, Integer.class); System.out.println(count);
}
2、创建jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.username=root
jdbc.password=root
3、配置Spring的配置文件
<!--引入jdbc.properties--><context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><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><bean class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean>
4、测试
1、在测试类装配 JdbcTemplate
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-jdbc.xml")
public class JdbcTemplateTest {@Autowiredprivate JdbcTemplate jdbcTemplate;}
2、测试增删改功能
@Test
//测试增删改功能
public void testUpdate(){ String sql = "insert into t_emp values(null,?,?,?)"; int result = jdbcTemplate.update(sql, "张三", 23, "男"); System.out.println(result);
}
3、查询一条数据为实体类对象
@Test
//查询一条数据为一个实体类对象
public void testSelectEmpById(){ String sql = "select * from t_emp where id = ?"; Emp emp = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Emp.class), 1); System.out.println(emp);
}
4、查询多条数据为一个 list 集合
@Test
//查询多条数据为一个list集合
public void testSelectList(){ String sql = "select * from t_emp"; List<Emp> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Emp.class)); list.forEach(emp -> System.out.println(emp));
}
5、查询单行单列的值
@Test
//查询单行单列的值
public void selectCount(){ String sql = "select count(id) from t_emp"; Integer count = jdbcTemplate.queryForObject(sql, Integer.class); System.out.println(count);
}
二、SpringTransactional(事务管理)
1. 事务类型
1.1. 编程式事务
事务功能的相关操作全部通过自己编写代码来实现:
Connection conn = ...; try { // 开启事务:关闭事务的自动提交 conn.setAutoCommit(false); // 核心操作 // 提交事务 conn.commit(); }catch(Exception e){ // 回滚事务 conn.rollBack(); }finally
{ // 释放数据库连接 conn.close(); }
缺陷
- 细节没有被屏蔽:具体操作过程中,所有细节都需要程序员自己来完成,比较繁琐
- 代码复用性不高: 如果没有有效抽取出来,每次实现功能都需要自己编写代码,代码就没有得到复用。
1.2. 声明式事务
既然事务控制的代码有规律可循,代码的结构基本是确定的,所以框架就可以将固定模式的代码抽取出来,进行
相关的封装封装起来后,我们只需要在配置文件中进行简单的配置即可完成操作
好处
- 好处1:提高开发效率
- 好处2:消除了冗余的代码
- 好处3:框架会综合考虑相关领域中在实际开发环境下有可能遇到的各种问题,进行了健壮性、性能等各个方
面的优化
1.3. 总结
所以,我们可以总结下面两个概念:
- 编程式:自己写代码实现功能
- 声明式:通过配置让框架实现功能
2. 基于注解的声明式事务
2.1. 准备工作
加入依赖
<dependencies><!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.1</version></dependency><!-- Spring 持久化层支持jar包 --><!-- 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><!-- 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>8.0.16</version></dependency><!-- 数据源 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.0.31</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.1</version></dependency></dependencies>
创建jdbc.properties
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.username=root
jdbc.password=root
配置Spring的配置文件
<!--扫描组件-->
<context:component-scan base-package="com.zheng.travel.spring.tx.annotation"></context:component-scan><!-- 导入外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties" /> <!-- 配置数据源 -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="url" value="${jdbc.url}"/> <property name="driverClassName" value="${jdbc.driver}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/>
</bean><!-- 配置 JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!-- 装配数据源 --> <property name="dataSource" ref="druidDataSource"/>
</bean>
创建表
CREATE TABLE t_book ( book_id int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', book_name varchar(20) DEFAULT NULL COMMENT '图书名称', price int(11) DEFAULT NULL COMMENT '价格', stock int(10) unsigned DEFAULT NULL COMMENT '库存(无符号)', PRIMARY KEY (book_id)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;insert into t_book(book_id,book_name,price,stock) values (1,'斗破苍穹',80,100),(2,'斗罗大陆',50,100);CREATE TABLE t_user ( user_id int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', username varchar(20) DEFAULT NULL COMMENT '用户名', balance int(10) unsigned DEFAULT NULL COMMENT '余额(无符号)', PRIMARY KEY (user_id)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
insert into t_user(user_id,username,balance) values (1,'admin',50);
创建组件
1、创建BookController
@Controller
public class BookController { @Autowired private BookService bookService; public void buyBook(Integer bookId, Integer userId){ bookService.buyBook(bookId, userId); }}
2、创建接口BookService
public interface BookService { void buyBook(Integer bookId, Integer userId);
}
3、创建实现类BookServiceImpl
@Service
public class BookServiceImpl implements BookService { @Autowired private BookDao bookDao; @Override public void buyBook(Integer bookId, Integer userId) { //查询图书的价格 Integer price = bookDao.getPriceByBookId(bookId); //更新图书的库存 bookDao.updateStock(bookId); //更新用户的余额 bookDao.updateBalance(userId, price); }}
4、创建接口BookDao:
public interface BookDao { Integer getPriceByBookId(Integer bookId); void updateStock(Integer bookId); void updateBalance(Integer userId, Integer price);
}
5、创建实现类BookDaoImpl
@Repository
public class BookDaoImpl implements BookDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public Integer getPriceByBookId(Integer bookId) { String sql = "select price from t_book where book_id = ?"; return jdbcTemplate.queryForObject(sql, Integer.class, bookId); } @Override public void updateStock(Integer bookId) { String sql = "update t_book set stock = stock - 1 where book_id = ?"; jdbcTemplate.update(sql, bookId); } @Override public void updateBalance(Integer userId, Integer price) { String sql = "update t_user set balance = balance - ? where user_id = ?"; jdbcTemplate.update(sql, price, userId); }}
2.2. 测试无事务情况
创建测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:tx-annotation.xml")
public class TxByAnnotationTest { @Autowired private BookController bookController; @Test public void testBuyBook(){ bookController.buyBook(1, 1); }}
模拟场景
用户购买图书,先查询图书的价格,再更新图书的库存和用户的余额
假设用户id为1的用户,购买id为1的图书用户余额为50,而图书价格为80
购买图书之后,用户的余额为-30,数据库中余额字段设置了无符号,因此无法将-30插入到余额字段
此时执行sql语句会抛出SQLException
观察结果
因为没有添加事务,图书的库存更新了,但是用户的余额没有更新
显然这样的结果是错误的,购买图书是一个完整的功能,更新库存和更新余额要么都成功要么都失败
2.3. 加入事务
添加事务配置
在Spring的配置文件中添加配置:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property>
</bean><!-- 开启事务的注解驱动 通过注解@Transactional所标识的方法或标识的类中所有的方法,都会被事务管理器管理事务--><!-- transaction-manager属性的默认值是transactionManager,如果事务管理器bean的id正好就是这个默认值,则可以省略这个属性 --><tx:annotation-driven transaction-manager="transactionManager" />
注意:导入的名称空间需要 tx 结尾的那个
添加事务注解
因为service层表示业务逻辑层,一个方法表示一个完成的功能,因此处理事务一般在service层处理
在BookServiceImpl的buybook()添加注解@Transactional
观察结果
由于使用了Spring的声明式事务,更新库存和更新余额都没有执行
现在环境搭建完毕,增删改查了解完毕,就需要正式开始了解事务操做方案:
首先,我们要明确编程式事务和声明式事务两种概念
2.4. @Transactional注解标识的位置
@Transactional标识在方法上,则只会影响该方法
@Transactional标识的类上,则会影响类中所有的方法
2.5. 事务属性
例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行
- REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中,一般的选择(默认值)
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
- MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
- REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起
- NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
- NEVER:以非事务方式运行,如果当前存在事务,抛出异常
- NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作
- 超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置
- 是否只读:建议查询时设置为只读
1、只读:readOnly
介绍
对一个查询操作来说,如果我们把它设置成只读,就能够明确告诉数据库,
这个操作不涉及写操作,这样数据库就能够针对查询操作来进行优化
使用方式
@Transactional(readOnly = true)
public void buyBook(Integer bookId, Integer userId) { //查询图书的价格 Integer price = bookDao.getPriceByBookId(bookId); //更新图书的库存 bookDao.updateStock(bookId); //更新用户的余额 bookDao.updateBalance(userId, price); //System.out.println(1/0);
}
注意
对增删改操作设置只读会抛出下面异常:
Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modificationare not allowed
2、超时:timeout
介绍
事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库资源
而长时间占用资源,大概率是因为程序运行出现了问题(可能是Java程序或MySQL数据库或网络连接等等)
此时这个很可能出问题的程序应该被回滚,撤销它已做的操作,事务结束,把资源让出来,让其他正常程序可以
执行
简单来说就是:超时回滚,释放资源
使用方式
@Transactional(timeout = 3)
public void buyBook(Integer bookId, Integer userId) { try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } //查询图书的价格 Integer price = bookDao.getPriceByBookId(bookId); //更新图书的库存 bookDao.updateStock(bookId); //更新用户的余额 bookDao.updateBalance(userId, price); //System.out.println(1/0);
}
观察结果
执行过程中抛出异常:
org.springframework.transaction.TransactionTimedOutException: Transaction timed out:deadline was Fri Jun 04 16:25:39 CST 2022
3、回滚策略:四种
介绍
声明式事务默认只针对运行时异常回滚,编译时异常不回滚
可以通过@Transactional中相关属性设置回滚策略
- rollbackFor:需要设置一个Class类型的对象
- rollbackForClassName:需要设置一个字符串类型的全类名
- noRollbackFor:需要设置一个Class类型的对象
- rollbackFor:需要设置一个字符串类型的全类名
使用方式
@Transactional(noRollbackFor = ArithmeticException.class)
//@Transactional(noRollbackForClassName = "java.lang.ArithmeticException")
public void buyBook(Integer bookId, Integer userId) { //查询图书的价格 Integer price = bookDao.getPriceByBookId(bookId); //更新图书的库存 bookDao.updateStock(bookId); //更新用户的余额 bookDao.updateBalance(userId, price); System.out.println(1/0);
}
观察结果
虽然购买图书功能中出现了数学运算异常(ArithmeticException),但是我们设置的回滚策略是
当出现ArithmeticException不发生回滚,因此购买图书的操作正常执行
4、事务隔离级别:Isolation
介绍
数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题,
一个事务与其他事务隔离的程度称为隔离级别,SQL标准中规定了多种事务隔离级别,
不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱
四种隔离级别
- 读未提交:READ UNCOMMITTED允许Transaction01读取Transaction02未提交的修改。
- 读已提交:READ COMMITTED\要求Transaction01只能读取Transaction02已提交的修改。
- 可重复读:REPEATABLE READ确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。
- 串行化:SERIALIZABLE确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
各个隔离级别解决并发问题的能力见下表:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
READ UNCOMMITTED | 有 | 有 | 有 |
READ COMMITTED | 无 | 有 | 有 |
REPEATABLE READ | 无 | 无 | 有 |
SERIALIZABLE | 无 | 无 | 无 |
各种数据库产品对事务隔离级别的支持程度:
隔离级别 | Oracle | MySQL |
READ UNCOMMITTED | × | √ |
READ COMMITTED | √(默认) | √ |
REPEATABLE READ | × | √(默认) |
SERIALIZABLE | √ | √ |
使用方式
@Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别
@Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交
@Transactional(isolation = Isolation.READ_COMMITTED)//读已提交
@Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读
@Transactional(isolation = Isolation.SERIALIZABLE)//串行化
5、事务传播行为:propagation
介绍
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播,
测试
1、创建接口CheckoutService
public interface CheckoutService { void checkout(Integer[] bookIds, Integer userId);
}
2、创建实现类CheckoutServiceImpl
@Service
public class CheckoutServiceImpl implements CheckoutService { @Autowired private BookService bookService; @Override @Transactional //一次购买多本图书 public void checkout(Integer[] bookIds, Integer userId) { for (Integer bookId : bookIds) { bookService.buyBook(bookId, userId); } }
}
3、在BookController中添加方法
@Autowired
private CheckoutService checkoutService;public void checkout(Integer[] bookIds, Integer userId){ checkoutService.checkout(bookIds, userId);
}
在数据库中将用户的余额修改为100元
观察结果
可以通过@Transactional中的propagation属性设置事务传播行为
修改BookServiceImpl中buyBook()上,注解@Transactional的propagation属性
@Transactional(propagation = Propagation.REQUIRED)
默认情况,表示如果当前线程上有已经开启的事务可用,那么就在这个事务中运行
经过观察,购买图书的方法buyBook()在checkout()中被调用,checkout()上有事务注解,因此在此事务中执行
所购买的两本图书的价格为80和50,而用户的余额为100,因此在购买第二本图书时余额不足失败,导致整个
checkout()回滚,即只要有一本书买不了,就都买不了
@Transactional(propagation = Propagation.REQUIRES_NEW)
表示不管当前线程上是否有已经开启的事务,都要开启新事务
同样的场景,每次购买图书都是在buyBook()的事务中执行,因此第一本图书购买成功,事务结束,
第二本图书购买失败,只在第二次的buyBook()中回滚,购买第一本图书不受影响,即能买几本就买几本
6、总结
基于注解的声明式配置
依赖导入,在导入引入spring-orm包的时候就已经导入相关配置,现在我们其它的数据源环境搭建完毕后,就可
以进行事务配置,我们在在Spring的配置文件中添加配置。
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property></bean><tx:annotation-driven transaction-manager="transactionManager" />
transaction-manager属性的默认值是transactionManager,
如果事务管理器bean的id正好就是这个默认值,则可以省略这个属性,
我们需要导入名称空间为 tx 结尾的那个,开启事务的注解驱动,
从而通过注解@Transactional所标识的方法或标识的类中所有的方法,都会被事务管理器管理事务。
@Transactional注解标识的位置,
如果@Transactional标识在方法上,则只会影响该方法
如果@Transactional标识的类上,则会影响类中所有的方法
3. 基于XML的Transactional
3.1. 场景模拟
参考基于注解的声明式事务
3.2. 修改 Spring 配置文件
将Spring配置文件中去掉tx:annotation-driven 标签,并添加配置:
<aop:config> <!-- 配置事务通知和切入点表达式 --> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.yjxz.spring.tx.xml.service.impl.*.*(..))"></aop:advisor></aop:config><!-- tx:advice标签:配置事务通知 -->
<!-- id属性:给事务通知标签设置唯一标识,便于引用 -->
<!-- transaction-manager属性:关联事务管理器 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- tx:method标签:配置具体的事务方法 --> <!-- name属性:指定方法名,可以使用星号代表多个字符 --> <tx:method name="get*" read-only="true"/> <tx:method name="query*" read-only="true"/> <tx:method name="find*" read-only="true"/> <!-- read-only属性:设置只读属性 --> <!-- 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"/> </tx:attributes></tx:advice>
注意:基于xml实现的声明式事务,必须引入aspectJ的依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.1</version></dependency>
相关文章:

Spring篇(事务篇 - 基础介绍)
目录 一、JdbcTemplate(持久化技术) 1. 简介 2. 准备工作 2.1. 引入依赖坐标 2.2. 创建jdbc.properties 2.3. 配置Spring的配置文件 3. 测试 3.1. 在测试类装配 JdbcTemplate 3.2. 测试增删改功能 查询一条数据为实体类对象 查询多条数据为一个…...

qt EventFilter用途详解
一、概述 EventFilter是QObject类的一个事件过滤器,当使用installEventFilter方法为某个对象安装事件过滤器时,该对象的eventFilter函数就会被调用。通过重写eventFilter方法,开发者可以在事件处理过程中进行拦截和处理,实现对事…...

[ 钓鱼实战系列-基础篇-6 ] 一篇文章让你了解邮件服务器机制(SMTP/POP/IMAP)-1
🍬 博主介绍 👨🎓 博主介绍:大家好,我是 _PowerShell ,很高兴认识大家~ ✨主攻领域:【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 🎉点赞➕评论➕收藏 养成习…...
wordpress伪静态规则
WordPress 伪静态规则是指将 WordPress 生成的动态 URL 转换为静态 URL 的规则,这样做可以提高网站的搜索引擎优化(SEO)效果,并且使得 URL 更加美观、易于记忆。伪静态规则通常需要在服务器的配置文件中设置,不同的服务器环境配置方法有所不同…...

缓存框架JetCache源码解析-缓存定时刷新
作为一个缓存框架,JetCache支持多级缓存,也就是本地缓存和远程缓存,但是不管是使用着两者中的哪一个或者两者都进行使用,缓存的实时性一直都是我们需要考虑的问题,通常我们为了尽可能地保证缓存的实时性,都…...

docker配置mysql8报错 ERROR 2002 (HY000)
通过docker启动的mysql,发现navicat无法连接,后来进入容器内部也是无法连接,产生以下错误 root9f3b90339a14:/var/run/mysqld# mysql -u root -p Enter password: ERROR 2002 (HY000): Cant connect to local MySQL server through socket …...

【Linux】为什么环境变量具有全局性?共享?写时拷贝优化?
环境变量表具有全局性的原因: 环境变量表之所以具有全局性的特征,主要是因为它们是在进程上下文中维护的,并且在大多数操作系统中,当一个进程创建另一个进程(即父进程创建子进程)时,子进程会继承…...
如何在Linux中找到MySQL的安装目录
前言 发布时间:2024-10-22 在日常管理和维护数据库的过程中,了解MySQL的确切安装位置对于执行配置更改、更新或者进行故障排查是非常重要的。本文将向您介绍几种在Linux环境下定位MySQL安装路径的方法。 通过命令行工具快速定位 使用 which 命令 首…...
机器人备件用在哪些领域
机器人备件,作为机器人技术的重要组成部分,被广泛应用于多个领域,以提高生产效率、降低成本、增强产品质量,并推动相关行业的智能化发展。以下是一些主要的应用领域: 制造业: 机器人备件在制造业中的应用最…...
基于单片机优先级的信号状态机设计
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、背景知识二、使用步骤1.定义相应状态和信号列表2.获取最高优先级信号3.通用状态机实现4.灯的控制函数 总结 前言 在嵌入式系统中,设备控制的灵…...
数字电路week3
数字电路学习 九.补充 1.Verilog和fpga verilog是一种描述电路的语言,出现于上世纪80年代 非:~与: &,或: |,异或: ^ fpga:一种可编程逻辑器件 FPGA 由大量的逻辑单元、查找表(LUTs)、触发…...

如何在 Linux 中对 USB 驱动器进行分区
如何在 Linux 中对 USB 驱动器进行分区 一、说明 为了在 Linux 上访问 USB 驱动器,它需要有一个或多个分区。由于 USB 驱动器通常相对较小,仅用于临时存储或轻松传输文件,因此绝大多数用户会选择只配置一个跨越整个 USB 磁盘的分区。但是&a…...
【STM32+HAL】STM32CubeMX学习目录
一、基础配置篇 【STM32HAL】微秒级延时函数汇总-CSDN博客 【STM32HAL】CUBEMX初始化配置 【STM32HAL】定时器功能小记-CSDN博客 【STM32HAL】PWM呼吸灯实现 【STM32HAL】DACDMA输出波形实现-CSDN博客 【STM32HAL】ADCDMA采集(单通道多通道)-CSDN博客 【STM32HAL】三重A…...

PPT自动化:Python如何修改PPT文字和样式!
文章目录 📖 介绍 📖🏡 演示环境 🏡📒 文章内容 📒📝 使用 Python 修改 PPT 文本内容📝 遍历所有幻灯片和文本框📝 设置和修改文本样式📝 复制和保留文本样式⚓️ 相关链接 ⚓️📖 介绍 📖 在日常工作中,PPT 的文字内容和样式修改似乎是一项永无止境的…...

4:Java的介绍与基础4:for语句
4.1for循环 for循环也是一个非常重要的东西,再代码中是一个循环的作用,在python的文章中也介绍过了for循环的使用方法,其实在Java中也是一样的逻辑,但是有着不一样的表达,现在我们来讲一下关于for循环的东西。 因为循…...

R语言机器学习算法实战系列(十二)线性判别分析分类算法 (Linear Discriminant Analysis)
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍LDA的原理LDA的步骤教程下载数据加载R包导入数据数据预处理数据描述数据切割构建模型预测测试数据评估模型模型准确性混淆矩阵模型评估指标ROC CurvePRC Curve保存模型总结优点:缺…...
[LeetCode] 50. Pow(x, n)
题目描述: 实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,xn )。 示例 1: 输入:x 2.00000, n 10 输出:1024.00000示例 2: 输入:x 2.10000, n 3 输出…...
Vue学习笔记(七、事件修饰符 .stop .capture .self .once .prevent)
先看一段基本的代码: <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>VueBaseCode</title><script src"./lib/vue.js"></script><style>.inner {width:…...

web网站搭建(静态)
准备工作: 关闭防火墙: [rootlocalhost ~]# systemctl disable --now firewalld 修改enforce为permissive [rootlocalhost ~]# setenforce 0 [rootlocalhost ~]# geten getenforce getent [rootlocalhost ~]# getenforce Permissive 重启服务 [rootloca…...
高效特征选择策略:提升Python机器学习模型性能的方法
高效特征选择策略:提升Python机器学习模型性能的方法 目录 🔍 特征选择的重要性📊 相关性分析🔄 递归特征消除 (RFE)🌳 基于模型的特征选择 1. 🔍 特征选择的重要性 特征选择在机器学习中至关重要&#…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...