前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring 全家桶之 Spring Framework 5.3(七)- 声明式事务

Spring 全家桶之 Spring Framework 5.3(七)- 声明式事务

作者头像
RiemannHypothesis
发布2022-08-19 15:39:41
4490
发布2022-08-19 15:39:41
举报
文章被收录于专栏:Elixir

「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战

一、基本环境搭建

创建数据库

代码语言:javascript
复制
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for account
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
  `username` varchar(50) NOT NULL,
  `balance` int(11) DEFAULT NULL,
  PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=gb2312;

-- ----------------------------
-- Records of account
-- ----------------------------
BEGIN;
INSERT INTO `account` VALUES ('Stark', 10000);
INSERT INTO `account` VALUES ('Peter', 10000);
COMMIT;

-- ----------------------------
-- Table structure for book
-- ----------------------------
DROP TABLE IF EXISTS `book`;
CREATE TABLE `book` (
  `isbn` varchar(50) NOT NULL,
  `book_name` varchar(100) DEFAULT NULL,
  `price` int(11) DEFAULT NULL,
  PRIMARY KEY (`isbn`)
) ENGINE=InnoDB DEFAULT CHARSET=gb2312;

-- ----------------------------
-- Records of book
-- ----------------------------
BEGIN;
INSERT INTO `book` VALUES ('ISBN-001', 'book01', 100);
INSERT INTO `book` VALUES ('ISBN-002', 'book02', 100);
INSERT INTO `book` VALUES ('ISBN-003', 'book03', 100);
INSERT INTO `book` VALUES ('ISBN-004', 'book04', 100);
INSERT INTO `book` VALUES ('ISBN-005', 'book05', 100);
COMMIT;

-- ----------------------------
-- Table structure for book_stock
-- ----------------------------
DROP TABLE IF EXISTS `book_stock`;
CREATE TABLE `book_stock` (
  `isbn` varchar(50) NOT NULL,
  `stock` int(11) DEFAULT NULL,
  PRIMARY KEY (`isbn`)
) ENGINE=InnoDB DEFAULT CHARSET=gb2312;

-- ----------------------------
-- Records of book_stock
-- ----------------------------
BEGIN;
INSERT INTO `book_stock` VALUES ('ISBN-001', 1000);
INSERT INTO `book_stock` VALUES ('ISBN-002', 1000);
INSERT INTO `book_stock` VALUES ('ISBN-003', 1000);
INSERT INTO `book_stock` VALUES ('ISBN-004', 1000);
INSERT INTO `book_stock` VALUES ('ISBN-005', 1000);
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

创建一个Maven项目spring-declaration-transaction,增加maven依赖

代码语言:javascript
复制
<properties>
    <spring-version>5.3.13</spring-version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring-version}</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>

    <!--spring jdbc依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring-version}</version>
    </dependency>
    <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.1.14</version>
    </dependency>
</dependencies>

增加数据库链接信息配置文件及application.xml配置文件

代码语言:javascript
复制
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/tx?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai&amp;allowPublicKeyRetrieval=true
username=root
password=root
initialSize=5
maxActive=20
代码语言:javascript
复制
<?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:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-4.3.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">


    <context:component-scan base-package="com.citi"/>
    <!--引用外部配置文件-->
    <context:property-placeholder location="classpath:database.properties"></context:property-placeholder>

    <!--数据库连接池配置-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driverClassName}"/>
        <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}"/>
    </bean>

    <!--配置JDBC Template,注入Spring容器中-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>

</beans>

二、事务初识

以用户买书为例,在项目中业务代码

首先在dao包中新增BookDao,包含了三个方法,用户购买图书后扣除用户余额及图书的库存,以及根据购买的图书id获取图书的价格

代码语言:javascript
复制
@Repository
public class BookDao {

    @Resource
    JdbcTemplate jdbcTemplate;

    /**
     * 减余额
     */
    public void updateBalance(String username, int price){
        String updateBanlanceSql = "UPDATE account SET balance=balance-? WHERE username=?";
        jdbcTemplate.update(updateBanlanceSql,price,username);
    }

    // 获取图书的价格
    public int getPrice(String isbn){
        String getPriceSql = "SELECT price FROM book WHERE isbn=?";
        return jdbcTemplate.queryForObject(getPriceSql,Integer.class,isbn);

    }

    // 减库存
    public void updateStock(String isbn){
        String updateStockSql = "UPDATE book_stock SET stock=stock-1 WHERE isbn=?";
        jdbcTemplate.update(updateStockSql,isbn);
    }
}

在service包中新增BookService,包含结账checkout方法

代码语言:javascript
复制
@Service
public class BookService {

    @Resource
    BookDao bookDao;


    // 用户买书结账
    public void checkout(String username, String isbn){

        // 1.减库存
        bookDao.updateStock(isbn);

        // 2.减余额
        int price = bookDao.getPrice(isbn);
        bookDao.updateBalance(username,price);

    }
}

对BookService进行测试,数据库中所有账户余额均为10000,所有图书价格为100,库存为1000

代码语言:javascript
复制
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:application.xml")
public class BookServiceTest {


    @Resource
    private BookService bookService;

    @Test
    public void testCheckoutSuccess() {

        bookService.checkout("Stark","ISBN-001");
        System.out.println("Checkout Success!");
    }
}

执行testCheckoutSuccess,控制它打印出Checkout Success!,数据库用户余额和图书库存扣减成功

image.png
image.png
image.png
image.png

对于checkout方法,只有余额扣减和库存扣减同时成功才算是成功,余额扣减和库存扣减任何一个失败,整个checkout事务都是失败的,余额扣减和库存扣减是作为一个整体

三、声明式事务

声明式事务:以前通过复杂编程来编写一个事务,替换为只需要告诉Spring哪个方法是事务方法即可,由Spring进行事务控制,基于Spring AOP环绕通知。

事务管理器代码的固定模式作为一种横切关注点,可以通过Spring AOP方法模块化,借助Spring AOP框架实现生命是事务管理,事务切面即事务管理器

不同的数据库连接使用不同的事务管理器

xml中配置事务管理器,导入tx名称空间xmlns:tx="http://www.springframework.org/schema/tx"

代码语言:javascript
复制
<!--事务管理器配置-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启基于注解的配置模式-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager" />

验证事务

在BookService的checkout方法中增加异常代码

代码语言:javascript
复制
public void checkout(String username, String isbn){

    // 1.减库存
    bookDao.updateStock(isbn);

    // 异常代码
    System.out.println(10/0);

    // 2.减余额
    int price = bookDao.getPrice(isbn);
    bookDao.updateBalance(username,price);
}

执行测试方法,检查数据库,库存被扣减1,而余额没有变化

image.png
image.png
image.png
image.png

将库存恢复至1000,余额恢复至10000,在checkout方法上增加@Transactional注解,再次测试 控制台报错,余额和库存数量没有变化,删除checkout方法中的异常代码,再次进行测试。

image.png
image.png
image.png
image.png

均可以正常扣减

四、@Transactional注解

@Transactional注解的属性

image.png
image.png
  • isolation:设置事务隔离级别
  • propagation:设置事务传播行为
  • noRollbakcFor:设置哪些异常事务不回滚,指定异常的class,是一个数组
  • noRollbackForClassName:设置哪些事务不回滚,指定异常的全类名,是一个数组
  • rollbackFor:设置哪些异常事务回滚,指定异常的class,是一个数组
  • rollbackForClassName:设置哪些异常事务回滚,指定异常的全类名,是一个数组
  • readOnly:布尔类型,设置事务为只读事务
  • timeout:超时时间,事务执行时间超出设定的时间自动终止并回滚

timeout

int类型,单位是秒,超时时间,事务执行超过指定的时间会自动停止并回滚

代码语言:javascript
复制
@Transactional(timeout = 3)
public void checkout(String username, String isbn){
    // 1.减库存
    bookDao.updateStock(isbn);
    
    // 休眠5s
    try {
        Thread.sleep(5000);
    } catch (Exception e){
        e.printStackTrace();
    }
    
    // 2.减余额
    int price = bookDao.getPrice(isbn);
    bookDao.updateBalance(username,price);
}

执行测试方法,库存数和余额数无变化

readOnly

默认为false,readOnly=true可以设置事务为只读事务,实现对事务进行优化,提高查询速度,忽略事务的commit等操作

代码语言:javascript
复制
@Transactional(readOnly = true)
public void checkout(String username, String isbn){
    // 1.减库存
    bookDao.updateStock(isbn);

    // 2.减余额
    int price = bookDao.getPrice(isbn);
    bookDao.updateBalance(username,price);
}

执行测试方法

image.png
image.png

事务中有更新操作,是不能设置readOnly的

noRollbackFor 和 noRollbackForClassName

  • 运行时异常,可以不用处理,默认都回滚
  • 编译时异常,使用try-catch处理或者在方法上声明throws,默认不回滚

noRollbackFor可以配置指定异常不回滚,即让原来默认回滚的异常不回滚 noRollbackForClassName指定不回滚的异常的全类名,noRollbackFor指定的是异常的类型,它们都是数组

代码语言:javascript
复制
//@Transactional(noRollbackForClassName = {"java.lang.ArithmeticException"})
@Transactional(noRollbackFor = {ArithmeticException.class})
public void checkout(String username, String isbn) {
    // 1.减库存
    bookDao.updateStock(isbn);

    // 2.减余额
    int price = bookDao.getPrice(isbn);
    bookDao.updateBalance(username, price);

    // 增加运行时异常,默认回滚
    System.out.println(10 / 0);

}

将stock恢复至1000,余额恢复至10000,执行测试

image.png
image.png
image.png
image.png

余额和库存都进行了扣减,实现了让原本默认回滚的不回滚

rollBackFor 和 rollBackForClassName

指定让原本不回滚的异常回滚,所有的编译时异常默认不会滚

代码语言:javascript
复制
// @Transactional(rollbackForClassName = {"java.io.FileNotFoundException"})
@Transactional(rollbackFor = {FileNotFoundException.class})
public void checkout(String username, String isbn) throws FileNotFoundException {
    // 1.减库存
    bookDao.updateStock(isbn);

    // 2.减余额
    int price = bookDao.getPrice(isbn);
    bookDao.updateBalance(username, price);

    // 增加编译时异常,默认不回滚
    new FileInputStream("../stark.txt");
}

将stock恢复至1000,余额恢复至10000,执行测试

image.png
image.png
image.png
image.png

余额和库存没有变化

isolation

数据库事务并发问题 假设现在有两个事务,t1和t2并发执行

事务隔离级别

数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。SQL标准中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。

①读未提交:READ UNCOMMITTED

  • 允许t1读取t2未提交的修改,这就导致了脏读。
  • t2提交,t1再次读取,读取到的数据是修改过的数据,与上一次读取到的数据不一致,这就导致了不可重复读
  • t2向表中插入一些新的book信息,t1查询所有,可以查到新增加的数据,这就导致了幻读 ②读已提交:READ COMMITTED
  • t1只能读取t2已提交的修改,避免了脏读 ③可重复读:REPEATABLE READ
  • 确保t1可以多次从一个字段中读取到相同的值,即t1执行期间禁止其它事务对这个字段进行更新。 ④串行化:SERIALIZABLE
  • 确保t1可以多次从一个表中读取到相同的行,在t1执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。

mysql查询隔离级别的命令

修改MySQL隔离级别 SET [SESSION(当前会话) | GLOBAL(全局)] TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE } 如:设置当前会话的事务隔离级别为读未提交 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 查询MySQL的隔离级别 SELECT @@global.tx_isolation; //查询全局隔离级别 SELECT @@session.tx_isolation;//查询当前会话隔离级别 SELECT @@tx_isolation;//同上 事务操作 开启事务 start transaction; 提交事务 commit; 回滚事务 rollback;

读未提交READ_UNCOMMITTED

BookService中增加getPrice方法,设置isolation属性为READ_UNCOMMITTED

代码语言:javascript
复制
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public int getPrice(String isbn){
    return bookDao.getPrice(isbn);
}

BookServiceTest中增加测试方法

代码语言:javascript
复制
// 读未提交测试
@Test
public void getPrice() {
    int price = bookService.getPrice("ISBN-001");
    System.out.println("价格:" + price);
}

首先执行查询图书价格测试,查询到的图书价格为100

image.png
image.png

然后在命令行中开启一个事务,修改图书价格,不提交

image.png
image.png

再次执行测试

image.png
image.png

控制台输出价格为200,读取到了命令行中未提交的脏数据

读已提交READ_COMMITTED

首先恢复price为100,将代码中隔离级别修改为READ_COMMITTED,此时新打开一个命令行窗口,执行修改图书价格为200的SQL,再次执行getPrice方法的测试

image.png
image.png
image.png
image.png

在未提交的情况下,READ_COMMITTED隔离级别读取到的数据仍然是修改前的数据,此时在命令行窗口执行commit提交修改命令,再次执行测试

image.png
image.png
image.png
image.png

读取到了提交修改后的值,避免了脏读,但是前后两次读取到的数据不一致导致了不可重复读

可重复读REPEATABLE-READ

任何时候读取都是一样的,打开两个命令行窗口,首先在第一个命令行中开启事务并设置隔离级别为REPEATABLE-READ,并查询一次price,结果为100;接着在第二个命令行中更新并提交price,在第一个命令行中再次查询price,结果仍然为100;在第二个命令行中执行删除并提交的操作,在第一个命令行中再次查询price,结果仍然为100;这就是可重复读,在一个会话SESSION中,读取到的数据自始至终都是一样的,避免了脏读和不可重复读。

image.png
image.png

plus:并发修改数据时会出现排队的现象,只有等待另一个的修改commit之后,才能继续修改。这种现象与隔离级别无关。

image.png
image.png

有事务的业务逻辑,容器中保存的是这个业务逻辑的代理对象,只有代理对象才可以执行事务

代码语言:javascript
复制
@Test
public void getClazz(){
    System.out.println(bookService.getClass());
}
image.png
image.png

propagation

事务传播行为,如果有多个事务嵌套运行,子事务是否要和上层事务或者已存在的事务共享同一个事务

  • REQUIRED: 支持当前事务,没有则新建
  • REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起
  • SUPPORTS:如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中
  • NOT_SUPPORTS:当前的方法不应该运行在事务中,如果有运行的事务,将它挂起
  • MANDATORY:当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常
  • NEVER:当前方法不应该运行在事务中,如果运行在事务中就抛出异常
  • NESTED:如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则就启动一个新的事务,并在它自己的事务内运行

以上属性,只有REQUIRED和REQUIRES_NEW是最常用的。

BookDao中新增方法,更新图书价格

代码语言:javascript
复制
public void updatePrice(String isbn, int price){
    String updatePriceSql = "UPDATE book SET price=? WHERE isbn=?";
    jdbcTemplate.update(updatePriceSql,price,isbn);
}

BookService中调用BookDao的updatePrice方法,并增加事务@Transactional

代码语言:javascript
复制
@Transactional
public void updatePrice(String isbn,int price){
    bookDao.updatePrice(isbn, price);
}

新增一个ComplexService,注入BookService,新增一个collaborateTransaction方法,该方法中调用了BookService的checkout方法和updatePrice方法,并增加@Transactional注解,这样就形成了collaborateTransaction一个大的事务里面嵌套了checkout和updatePrice两个小的事务,当其中collaborateTransaction发生异常或者checkout或者updatePrice发生异常,其他事务是否会回滚呢?

代码语言:javascript
复制
@Service
public class ComplexService {

    @Resource
    private BookService bookService;

    // 综合事务
    @Transactional
    public void collaborateTransaction(String username,String isbn, int price){
        // 事务1,事务2失败事务1是否需要回滚?可设置是否回滚,这就是事务传播行为,
        // 是否和上层事务共享一个事务
        bookService.checkout(username,isbn);

        // 事务2
        bookService.updatePrice(isbn,price);

    }
}
REQUIRED

给BookService的checkout方法和updatePrice设置传播行为,即给@Transactional注解增加属性propagation = Propagation.REQUIRED,checkout的@Transactional注解中其他演示过的属性可以删除,REQUIRED的意思是该方法需要事务,如果存在事务就使用已存在的事务,如果没有就新建一个事务

新增一个测试类ComplexServiceTest, 继承BookServiceTest

代码语言:javascript
复制
public class ComplexServiceTest extends BookServiceTest{

    @Resource
    private ComplexService complexService;

    @Test
    public void collaborateTransaction() {
        complexService.collaborateTransaction("Stark","ISBN-002",200);
    }
}

恢复默认数据,所有book的price改为100,stock都改为1000,并且在updatePrice方法中增加异常代码

代码语言:javascript
复制
// 异常代码
System.out.println(10/0);

执行测试,查看数据库,price、stock、balance数据都没有变化,可以确定发生异常后chekcout和update都进行了回滚

REQUIRES_NEW

修改checkout的事务属性为Propagation.REQUIRES_NEW,即创建一个新事务,不与其他方法共享事务,发生异常时其他事务不会回滚,再次执行测试。

image.png
image.png

可以发现price价格不变,updatePrice发生了回滚

image.png
image.png

stock库存数量减少,checkout正常执行并没有进行回滚

将collaborateTransaction方法中的checkout方法和updatePrice方法都改为REQIURES_NEW,并且在collaborateTransaction中增加异常,注释updatePrice方法中的异常,恢复初始数据后执行测试

image.png
image.png

price发生变化,updatePrice方法没有回滚

image.png
image.png

stock库存数量减少,checkout方法也没有回滚

这是因为两个事务是新的事务,与上层方法的事务不属于同一个事务,所有上层方法出现异常并不会影响这两个方法

plus:与上层方法共享事务时,该事物本身设置的属性都失效,以上层事务设置的属性为准。

五、基于XML的声明式事务

基于注解的声明式事务配置步骤

  1. Spring中提供事务管理器(事务切面),配置事务管理器
  2. 开启基于注解的声明式事务,依赖tx名称空间
  3. 需要事务的方法上添加注解

基于XML配置的声明式事务,依赖tx和aop名称空间

代码语言:javascript
复制
<!--基于XML配置的声明式事务-->
<aop:config>
    <!--切面-->
    <aop:pointcut id="txPoint" expression="execution(* com.citi.service.*.*(..))"/>
    <!--增强 advice-ref:指向事务管理器配置-->
    <aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"></aop:advisor>
</aop:config>

<!--配置事务管理器-->
<tx:advice id="myAdvice" transaction-manager="dataSourceTransactionManager">
    <tx:attributes>
        <!--指明哪些方法是事务方法-->
        <!--切入点表达式只是说事务管理器要切入这些方法,哪些方法加事务使用tx:method指定-->
        <tx:method name="checkout" propagation="REQUIRED" timeout="-1"/>
        <tx:method name="get*" read-only="true"></tx:method>
    </tx:attributes>
</tx:advice>

将所有方法上的@Transactional注解全部注释 执行BookServiceTest中的testCheckoutSuccess方法

代码语言:javascript
复制
@Test
public void testCheckoutSuccess() throws FileNotFoundException {

    bookService.checkout("Stark","ISBN-002");
    System.out.println("Checkout Success!");
}

执行后库存和余额都能正常扣除

在BookService的checkout方法增加异常代码

代码语言:javascript
复制
// 测试基于XML配置的声明式事务时的异常代码
int i = 10 / 0;

再次执行测试。查看数据库可以确定余额和库存发生了回滚,数据不变

最后基于注解的声明式事务配置和基于XML的声明式事务如何选择?

重要的事务使用配置或者当事务非常非常多的时候,不重要的事务使用注解

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-01-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、基本环境搭建
  • 二、事务初识
  • 三、声明式事务
    • 验证事务
    • 四、@Transactional注解
      • timeout
        • readOnly
          • noRollbackFor 和 noRollbackForClassName
            • rollBackFor 和 rollBackForClassName
              • isolation
                • 读未提交READ_UNCOMMITTED
                • 读已提交READ_COMMITTED
                • 可重复读REPEATABLE-READ
              • propagation
                • REQUIRED
                • REQUIRES_NEW
            • 五、基于XML的声明式事务
            相关产品与服务
            数据库
            云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档