当前位置: 首页 > news >正文

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. 声明式事务

既然事务控制的代码有规律可循,代码的结构基本是确定的,所以框架就可以将固定模式的代码抽取出来,进行

相关的封装封装起来后,我们只需要在配置文件中进行简单的配置即可完成操作

好处
  1. 好处1:提高开发效率
  2. 好处2:消除了冗余的代码
  3. 好处3:框架会综合考虑相关领域中在实际开发环境下有可能遇到的各种问题,进行了健壮性、性能等各个方

面的优化

1.3. 总结

所以,我们可以总结下面两个概念:

  1. 编程式:自己写代码实现功能
  2. 声明式:通过配置让框架实现功能

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. 事务属性

例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行

  1. REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中,一般的选择(默认值)
  2. SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
  3. MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
  4. REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起
  5. NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
  6. NEVER:以非事务方式运行,如果当前存在事务,抛出异常
  7. NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作
  8. 超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置
  9. 是否只读:建议查询时设置为只读
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中相关属性设置回滚策略

  1. rollbackFor:需要设置一个Class类型的对象
  2. rollbackForClassName:需要设置一个字符串类型的全类名
  3. noRollbackFor:需要设置一个Class类型的对象
  4. 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标准中规定了多种事务隔离级别,

不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱

四种隔离级别
  1. 读未提交:READ UNCOMMITTED允许Transaction01读取Transaction02未提交的修改。
  2. 读已提交:READ COMMITTED\要求Transaction01只能读取Transaction02已提交的修改。
  3. 可重复读:REPEATABLE READ确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。
  4. 串行化: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&#xff08;持久化技术&#xff09; 1. 简介 2. 准备工作 2.1. 引入依赖坐标 2.2. 创建jdbc.properties 2.3. 配置Spring的配置文件 3. 测试 3.1. 在测试类装配 JdbcTemplate 3.2. 测试增删改功能 查询一条数据为实体类对象 查询多条数据为一个…...

qt EventFilter用途详解

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

[ 钓鱼实战系列-基础篇-6 ] 一篇文章让你了解邮件服务器机制(SMTP/POP/IMAP)-1

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…...

wordpress伪静态规则

WordPress 伪静态规则是指将 WordPress 生成的动态 URL 转换为静态 URL 的规则&#xff0c;这样做可以提高网站的搜索引擎优化(SEO)效果&#xff0c;并且使得 URL 更加美观、易于记忆。伪静态规则通常需要在服务器的配置文件中设置&#xff0c;不同的服务器环境配置方法有所不同…...

缓存框架JetCache源码解析-缓存定时刷新

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

docker配置mysql8报错 ERROR 2002 (HY000)

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

【Linux】为什么环境变量具有全局性?共享?写时拷贝优化?

环境变量表具有全局性的原因&#xff1a; 环境变量表之所以具有全局性的特征&#xff0c;主要是因为它们是在进程上下文中维护的&#xff0c;并且在大多数操作系统中&#xff0c;当一个进程创建另一个进程&#xff08;即父进程创建子进程&#xff09;时&#xff0c;子进程会继承…...

如何在Linux中找到MySQL的安装目录

前言 发布时间&#xff1a;2024-10-22 在日常管理和维护数据库的过程中&#xff0c;了解MySQL的确切安装位置对于执行配置更改、更新或者进行故障排查是非常重要的。本文将向您介绍几种在Linux环境下定位MySQL安装路径的方法。 通过命令行工具快速定位 使用 which 命令 首…...

机器人备件用在哪些领域

机器人备件&#xff0c;作为机器人技术的重要组成部分&#xff0c;被广泛应用于多个领域&#xff0c;以提高生产效率、降低成本、增强产品质量&#xff0c;并推动相关行业的智能化发展。以下是一些主要的应用领域&#xff1a; 制造业&#xff1a; 机器人备件在制造业中的应用最…...

基于单片机优先级的信号状态机设计

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、背景知识二、使用步骤1.定义相应状态和信号列表2.获取最高优先级信号3.通用状态机实现4.灯的控制函数 总结 前言 在嵌入式系统中&#xff0c;设备控制的灵…...

数字电路week3

数字电路学习 九.补充 1.Verilog和fpga verilog是一种描述电路的语言&#xff0c;出现于上世纪80年代 非&#xff1a;~与: &&#xff0c;或: |&#xff0c;异或: ^ fpga&#xff1a;一种可编程逻辑器件 FPGA 由大量的逻辑单元、查找表&#xff08;LUTs&#xff09;、触发…...

如何在 Linux 中对 USB 驱动器进行分区

如何在 Linux 中对 USB 驱动器进行分区 一、说明 为了在 Linux 上访问 USB 驱动器&#xff0c;它需要有一个或多个分区。由于 USB 驱动器通常相对较小&#xff0c;仅用于临时存储或轻松传输文件&#xff0c;因此绝大多数用户会选择只配置一个跨越整个 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循环也是一个非常重要的东西&#xff0c;再代码中是一个循环的作用&#xff0c;在python的文章中也介绍过了for循环的使用方法&#xff0c;其实在Java中也是一样的逻辑&#xff0c;但是有着不一样的表达&#xff0c;现在我们来讲一下关于for循环的东西。 因为循…...

R语言机器学习算法实战系列(十二)线性判别分析分类算法 (Linear Discriminant Analysis)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍LDA的原理LDA的步骤教程下载数据加载R包导入数据数据预处理数据描述数据切割构建模型预测测试数据评估模型模型准确性混淆矩阵模型评估指标ROC CurvePRC Curve保存模型总结优点:缺…...

[LeetCode] 50. Pow(x, n)

题目描述&#xff1a; 实现 pow(x, n) &#xff0c;即计算 x 的整数 n 次幂函数&#xff08;即&#xff0c;xn &#xff09;。 示例 1&#xff1a; 输入&#xff1a;x 2.00000, n 10 输出&#xff1a;1024.00000示例 2&#xff1a; 输入&#xff1a;x 2.10000, n 3 输出…...

Vue学习笔记(七、事件修饰符 .stop .capture .self .once .prevent)

先看一段基本的代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>VueBaseCode</title><script src"./lib/vue.js"></script><style>.inner {width:…...

web网站搭建(静态)

准备工作&#xff1a; 关闭防火墙&#xff1a; [rootlocalhost ~]# systemctl disable --now firewalld 修改enforce为permissive [rootlocalhost ~]# setenforce 0 [rootlocalhost ~]# geten getenforce getent [rootlocalhost ~]# getenforce Permissive 重启服务 [rootloca…...

高效特征选择策略:提升Python机器学习模型性能的方法

高效特征选择策略&#xff1a;提升Python机器学习模型性能的方法 目录 &#x1f50d; 特征选择的重要性&#x1f4ca; 相关性分析&#x1f504; 递归特征消除 (RFE)&#x1f333; 基于模型的特征选择 1. &#x1f50d; 特征选择的重要性 特征选择在机器学习中至关重要&#…...

2024年TI杯E题-三子棋游戏装置方案分享-jdk123团队-第四弹 第一题

#1024程序员节&#xff5c;征文# 往期回顾 前期准备 摄像头bug解决 手搓机械臂 视觉模块的封装 第一问&#xff1a; 需要将一颗黑棋&#xff0c;放入棋盘中的五号位置。 理想思路&#xff1a;依据摄像头&#xff0c;依据机械臂及其传感器。建立机械臂的逆运动学方程。然后完…...

优化多表联表查询的常见方法归纳

目录 一、使用mybatis的嵌套查询 二、添加表冗余字段&#xff0c;减少联表查询需求 三、分表预处理&#xff0c;前端再匹配 一、使用mybatis的嵌套查询 【场景说明】 前端需要展示一张列表&#xff0c;其中的字段来源于多张表&#xff0c;如何进行查询优化&#xff1f; 【…...

Java毕业设计 基于SpringBoot发卡平台

Java毕业设计 基于SpringBoot发卡平台 这篇博文将介绍一个基于SpringBoot发卡平台&#xff0c;适合用于Java毕业设计。 功能介绍 首页 图片轮播 商品介绍 商品详情 提交订单 文章教程 文章详情 查询订单  查看订单卡密 客服   后台管理 登录 个人信息 修改密码 管…...

VRoid Studio 介绍 3D 模型编辑器

VRoid Studio 是由日本公司 pixiv 开发的一款免费 3D 模型创建软件&#xff0c;专门设计用于轻松制作 3D 虚拟角色。它的主要特点是用户友好&#xff0c;允许没有 3D 建模经验的用户创建高质量的 3D 人物角色&#xff0c;尤其是针对虚拟主播&#xff08;Vtuber&#xff09;、动…...

软件设计模式------抽象工厂模式

抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;&#xff0c;又称Kit模式&#xff0c;属于对象创建型模式。 一&#xff1a;先理解两个概念&#xff1a; &#xff08;1&#xff09;产品等级结构&#xff1a; 即产品的继承结构。 通俗来讲&#xff0c;就是不同品…...

基于springboot+微信小程序校园自助打印管理系统(打印1)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于springboot微信小程序校园自助打印管理系统实现了管理员、店长和用户。管理员实现了用户管理、店长管理、打印店管理、打印服务管理、服务类型管理、预约打印管理和系统管理。店长实现…...

解锁文本数据可视化的无限可能:Wordcloud库全解析

文章目录 **&#x1f31f;解锁文本数据可视化的无限可能&#xff1a;Wordcloud库全解析&#x1f510;**1. **背景介绍**2. **Wordcloud库是什么&#xff1f;**3. **如何安装Wordcloud库&#xff1f;**4. **Wordcloud库的基本函数使用方法**5. **实际应用场景**6. **常见问题及解…...

代码审计-Python Flask

1.Jinjia2模版注入 Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug &#xff0c;模板引擎则使用 Jinja2。jinja2是Flask作者开发的一个模板系统&#xff0c;起初是仿django模板的一个模板引擎&#xff0c;为Flask提供模板支持&#xff0c;由于…...

深度学习:开启人工智能的新纪元

深度学习&#xff1a;开启人工智能的新纪元 深度学习是机器学习的一个子领域&#xff0c;它基于人工神经网络的学习算法&#xff0c;特别是那些具有多个非线性变换的层&#xff08;即“深度”&#xff09;。这些算法模仿人脑处理信息的方式&#xff0c;通过学习数据的多层次表…...

第十四章_File类与IO流

目录 1. java.io.File类的使用 1.1 概述 1.2 构造器 1.3 常用方法 1、获取文件和目录基本信息 2、列出目录的下一级 3、File类的重命名功能 4、判断功能的方法 5、创建、删除功能 1.4 练习 2. IO流原理及流的分类 2.1 Java IO原理 2.2 流的分类 2.3 流的API 3. …...