1.问题点
service服务层中经常会出现各种其他服务的依赖调用,特别是在写操作相关场景下。
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("员工创建失败");
}
}
}