【spring】spring 的事务(transaction) 三 try catch对事务的影响


相关文章
spring 的事务(transaction) 一 基础概念介绍
spring 的事务(transaction) 二 陷阱
spring 的事务(transaction) 三 try catch对事务的影响
spring 的事务(transaction) 四 嵌套事务PROPAGATION_NESTED

概述

外层事务和内层事务基础概念知晓后,需要结合try catch才能真正理解,因为try catch会影响异常的传递。

本篇文章强调了外层和内存,以Propagation.REQUIRED+Propagation.REQUIRED为例,看过前面《spring 的事务(transaction) 一 基础概念介绍》一文的话,知道这二者是独立事务,而不是父子事务。针对父子事务,会单独写篇文章来介绍。

try catch可以加在内层,也可以在外层。 虽然外层和内存

1. 非异常用例

我们开始一个经典的嵌套用例。

1.1 创建工程

我们先创建一个spring的工程,利用spring 单元测试架构,来写用例

工程的创建参见 用SpringBoot创建mysql工程,后续以该工程为基础改造用例。

数据库脚本,新增一个:

CREATE TABLE `order` (
  `id` INT(11) NOT NULL,
  `price` VARCHAR(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO `order` (`id`, `price`) VALUES(1,'100');
INSERT INTO `order` (`id`, `price`) VALUES(2,'150');

项目整体视图:

在这里插入图片描述
MysqldemoApplication:

package com.example.mysqldemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@ComponentScan("com.example")   //设置扫描路径
@EnableTransactionManagement   //开启事务
public class MysqldemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(MysqldemoApplication.class, args);
	}

}


定义2个接口,分别是IOrderService和IUserService:

package com.example.service;

public interface IOrderService {

    public void  addOrder(int id, String name);

}

package com.example.service;

public interface IUserService {

    public void  addUser(int id, String name);

}

定义2个实现类:

package com.example.service;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderServiceImpl implements IOrderService {


    @Autowired
    private JdbcTemplate jdbcTemplate;


    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void addOrder(int id, String price) {
        //order是mysql关键词,必须用`号(tab键上方的那个键,波浪线键)包裹起来
        jdbcTemplate.execute("insert into `order` values ("+id+",'"+price+"')");

    }
}


package com.example.service;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl implements IUserService {


    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private IOrderService orderService;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void addUser(int id, String name) {
        jdbcTemplate.execute("insert into `student` values (" + id + ",'" + name + "')");
        try {
           //注意:此时有try catch
            orderService.addOrder(3, "110");
        } catch (Exception e) {
            System.out.println(e);
        }
    }

}


application.properties:

#集成mysql数据库的配置
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://10.40.65.183:3306/test
spring.datasource.username=
spring.datasource.password=

测试用例:

package com.example.mysqldemo;

import com.example.service.IUserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MysqldemoApplication.class)
public class MysqldemoApplicationTests {

	@Autowired
	private IUserService userService ;


	@Test
	public void insert() {
		userService.addUser(4,"hanmeimei");
	}

}

此时我们在此时用例中插入一个用户数据,而在内部,会插入一个订单信息。

如果数据库存在脏数据,可以先清除脏数据。

1.2 执行

执行后,数据都是正常插入的,原因是没有任何异常出现:
在这里插入图片描述
注意:此时addUser()内有try catch,这是标准用法:

    public void addUser(int id, String name) {
        jdbcTemplate.execute("insert into `student` values (" + id + ",'" + name + "')");
        try {

            orderService.addOrder(3, "110");
        } catch (Exception e) {
            System.out.println(e);
        }
    }

2. 内层抛出非check异常,外层进行捕获

结论:符合预期,内层回滚,外层不回滚。内层抛出非check异常,自动帮我们回滚内层事务,外层捕获内层异常,避免异常传递至外层,外层不回滚。

注意:我们定义的外层和内存事务都是REQUIRES_NEW,如果是其他事务,又是另一个结论。

我们模拟在orderservice.addOrder()内抛出一个ArithmeticException异常,这是个非check异常。

按照预期,内层事务出现非check异常,会自动回滚,而外层没有出现错误,不会回滚。

   System.out.println( 1/0);

在这里插入图片描述
重新执行:
在这里插入图片描述
总结:

  • 内层事务抛出非check异常,并且内部没有try catch,这样内层声明的事务能捕获该异常,自动帮我们回滚内层事务
  • 外层自身没有抛出异常,并且通过try catch ,捕获了内层异常,使得内部异常没有传递到外层,外层等价于没有异常,不会回滚

在这里插入图片描述

3. 内层抛出非check异常,外层不进行捕获

在2章节里面,我们就强调过 外层没有加try catch时,内存抛出异常后,会导致异常传递至外层,引发外层也回滚

下面我们来验证下上述结论:

注释掉外层方法的try catch:

    public void addUser(int id, String name) {
        jdbcTemplate.execute("insert into `student` values (" + id + ",'" + name + "')");


        orderService.addOrder(3, "110");

我们来验证下,确实都没有出现新增的数据:
在这里插入图片描述
过程分析:
在这里插入图片描述

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值