service事务失效(接口方法A调用类方法B)

1.问题点

service服务层中经常会出现各种其他服务的依赖调用,特别是在写操作相关场景下。

  • 场景 service事务失效(接口方法A调用类方法B)_第1张图片

  • 分析

spirng中事务的控制处理基于代理模式,而代理模式可以略分为三种

  • JDK静态代理(灵活性差,排除)
  • JDK动态代理(基于接口)
  • Cglib代理(基于类)

spring内部的代理采用的式JDK动态代理 + Cglib代理

简单来说,如果实现了某个接口,那么Spring就选择JDK代理(不一定),如果没有,那么就选择CGLIB代理,说起来简单,Spring还会闹脾气,不代理呢(如上图使用事务的场景)

2.案例解决

  • 案例表
  CREATE TABLE `dep` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    `dep_name` varchar(255) NOT NULL COMMENT '部门名称',
    `description` varchar(255) DEFAULT NULL COMMENT '描述',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_depname` (`dep_name`)
  ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

  CREATE TABLE `emp` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    `emp_name` varchar(255) NOT NULL COMMENT '名称',
    `dep_id` bigint(20) NOT NULL COMMENT '部门Id',
    `status` varchar(255) NOT NULL DEFAULT '1' COMMENT '状态 1.有效,2.无效',
    `description` varchar(255) DEFAULT NULL COMMENT '描述',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_name` (`emp_name`) USING BTREE
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

  CREATE TABLE `task` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    `task_name` varchar(255) NOT NULL,
    `task_status` varchar(255) NOT NULL DEFAULT '0' COMMENT '任务状态 0:进行中 1:成功 -1:失败',
    PRIMARY KEY (`id`)
  ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
  • 基础代码

可基于mybatis-plus进行基础代码等生成(此处略)

  • 配置(基于springboot + mybatis-plus)
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/tx_demo?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false
    username: root
    password: root
    druid:
      # 初始化大小,最小,最大
      initial-size: 10
      min-idle: 10
      maxActive: 20
      # 配置获取连接等待超时的时间
      maxWait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1 FROM DUAL
mybatis-plus:
  mapper-locations: classpath:mappers/*Mapper.xml
  typeAliasesPackage: com.base.transaction.model
  global-config:
    id-type: 0
    field-strategy: 1
    db-column-underline: true
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: false
    jdbc-type-for-null: 'null'
  • springboot项目启动类
  @SpringBootApplication
  @EnableTransactionManagement // 开启事务
  @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) // 处理事务问题,如类中非事务方法A调用事务方法B
  public class BaseTransactionApplication {

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

  }
  • controller代码
  @RestController
  @RequestMapping("/task")
  @Slf4j
  public class TaskController {

      @Autowired
      private TaskService taskService;

      @GetMapping("/add")
      public void addTask() {

          Task task = new Task();
          task.setTaskName(String.valueOf(new Random().nextInt(100)));

          taskService.save(task);

          log.info("task create success");

          // 更新任务状态
          taskService.insertDepAndEmpTask(task.getId());
      }
  }
  • service代码
  @Service
  @Slf4j
  public class TaskServiceImpl extends ServiceImpl implements TaskService {

      @Autowired
      private EmpService empService;

      @Autowired
      private DepService depService;

      @Autowired
      private TaskMapper taskMapper;

      @Override
      public void insertDepAndEmpTask(Long taskId) {
          try {
              ((TaskServiceImpl)AopContext.currentProxy()).insertDepAndEmp();

              Task task = new Task();
              task.setId(taskId);
              task.setTaskStatus("1");
              taskMapper.updateById(task);
              log.info("update task status success");
          } catch (Exception e) {
              Task task = new Task();
              task.setId(taskId);
              task.setTaskStatus("-1");
              taskMapper.updateById(task);
              log.info("update task status failed");
              //TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
              e.printStackTrace();
          }
      }

      @Transactional//(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
      public void insertDepAndEmp() {
          Dep dep = new Dep();
          dep.setDepName("技术部" + new Random().nextInt(100));
          dep.setDescription("这是一群屌丝的天下");

          // 1.创建部门
          boolean depSave = depService.save(dep);
          if (!depSave) {
              throw new RuntimeException("部门创建失败");
          }

          dep.setId(null);

          Emp emp = new Emp();
          emp.setDepId(dep.getId());
          emp.setEmpName("屌丝" + new Random().nextInt(100));
          emp.setDescription("我是一个屌丝程序员");

          // 2.创建员工
          boolean empSave = empService.save(emp);
          if (!empSave) {
              throw new RuntimeException("员工创建失败");
          }
      }
  }

转载于:https://my.oschina.net/u/2492007/blog/3069752

你可能感兴趣的:(service事务失效(接口方法A调用类方法B))