1 相关知识简介
1.1 DML和DDL的简介
1.1.1 DML简介
-
DML(Data Manipulation Language)数据操纵语言,用于操作数据库对象中包含的数据,操作的对象是记录。
-
主要命令:insert、delete、update。
1.1.2 DDL简介
-
DDL(Data Definition Language)数据定义语言,主要用于定义或改变表结构。用于定义和管理数据库中的所有对象的语言,对数据库中的某些对象(例如database,table)进行管理。操作对象包括数据库本身以及数据库对象,如表、视图等等。DDL操作是隐式提交的,不能回滚。
-
主要命令:create、alter、drop、truncate。
注意:DDL操作是隐性提交的!不能rollback 。
1.2 Spring支持的7种传播特性
使用@Transactional注解时,可以指定propagation参数。
Spring支持7种事务传播特性,如下:
事务行为 | 说明 |
---|---|
required | 支持当前事务;如果没有事务,新建一个事务 |
supports | 支持当前事务;如果没有事务,以非事务方式执行 |
mandatory | 支持当前事务;如果没有事务,抛异常 |
requires_new | 新建事务;如果当前存在事务,则挂起 |
not_supported | 以非事务方式运行;如果当前存在事务,则挂起 |
never | 以非事务方式运行;如果当前存在事务,抛异常 |
nested | 如果当前存在事务,则在嵌套事务内执行;无则创建新的事务 |
目前只有这三种传播特性才会创建新事务:required,requires_new,nested。
不支持事务的传播机制:supports、not_supported、never。如果配置了这三种传播方式的话,在发生异常的时候,事务是不会回滚的。
1.3 嵌套事务
1.3.1 propagation = Propagation.NESTED
嵌套事务内部事务和外部事务是有关联的,内部事务的提交和回滚依赖与外部事务,与外部事务是一个整体,即核心思想就是子事务不会独立提交,而是取决于父事务,当父事务提交,那么子事务才会随之提交;如果父事务回滚,那么子事务也回滚。
如果想实现内部事务回滚,外部事务顺利进行。可以将内部嵌套事务放在try/catch中,并且不继续往上抛异常。
1.3.2 propagation = Propagation.REQUIRES_NEW
Propagation.REQUIRES_NEW 内层事务执行完就立即提交,与外层事务不是一个整体。
可以参考以下内容:
(1)spring中12种@Transactional的失效场景(小结)_java_脚本之家
(2)【spring】spring 的事务(transaction) 四 嵌套事务PROPAGATION_NESTED_云川之下的博客-CSDN博客_嵌套事务
2 代码示例
示例代码:
@Service
public class UserServiceImpl implements userService {
@Autowired
private UserMapper userMapper;
@Transactional(rollbackFor = Exception.class)
@Override
public void add(User user) throws Exception {
// 步骤1
userMapper.insertUser(user);
String tableName = "user_1";
List<Attribute> columnList = new ArrayList<>();
columnList.addAll(user.getCode());
// 步骤2
userMapper.createTable(tableName,columnList);
// 步骤3
userMapper.insertUserRole(user.getRole());
}
}
结论:如果步骤2的DDL语句错误,步骤1不会回滚,因为DDL操作隐式提交。
?解决方案:(调整执行顺序)
@Service
public class UserServiceImpl implements userService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleService roleService;
@Transactional(rollbackFor = Exception.class)
@Override
public void add(User user) throws Exception {
String tableName = "user_1";
List<Attribute> columnList = new ArrayList<>();
columnList.addAll(user.getCode());
// 步骤2
userMapper.createTable(tableName,columnList);
// 步骤1
userMapper.insertUser(user);
// 步骤3
userMapper.insertUserRole(user.getRole());
}
}
步骤1和步骤2交换顺序后,如果步骤3出现错误,步骤1是可以回滚的,步骤2隐式提交。。。如何解决呢?
1. 手动补偿步骤2,代码如下:
@Service
public class UserServiceImpl implements userService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleService roleService;
@Transactional(rollbackFor = Exception.class)
@Override
public void add(User user) throws Exception {
String tableName = "user_1";
List<Attribute> columnList = new ArrayList<>();
columnList.addAll(user.getCode());
try{
// 步骤2
userMapper.createTable(tableName,columnList);
// 步骤1
userMapper.insertUser(user);
// 步骤3
userMapper.insertUserRole(user.getRole());
catch(Exception e){
// 查询表是否存在
int count = userMapper.selectTable(tableName);
if(count>0){
// 步骤4 删除步骤2创建的表
userMapper.dropTable(tableName);
}
// 解决异常被catch,造成事务失效
throw new RuntimeException("操作失败");
}
}
}
如果步骤3出错,步骤2被步骤4手动补偿掉,步骤1无法回滚,因为步骤4是DDL语句提交了事务,该如何解决呢?如下:
√最终解决方案:
可以将步骤3和步骤1放到别的类中以嵌套事物的方式,内部事务的回滚不影响外部事务(DDL不支持事务),代码如下:
方法一:(嵌套事务)
@Service
public class UserServiceImpl implements userService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleService roleService;
@Transactional(rollbackFor = Exception.class)
@Override
public void add(User user) throws Exception {
String tableName = "user_1";
List<Attribute> columnList = new ArrayList<>();
columnList.addAll(user.getCode());
// 步骤2
userMapper.createTable(tableName,columnList);
try{
roleService.doOtherThing();
catch(Exception e){
// 查询表是否存在
int count = userMapper.selectTable(tableName);
if(count>0){
// 步骤4 删除步骤2创建的表
userMapper.dropTable(tableName);
}
// 防止异常被catch掉事务失效
// throw new RuntimeException("操作错误");
}
}
}
@Service
public class RoleServiceImpl implements RoleService{
@Autowired
private UserMapper userMapper;
@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
@Override
public void doOtherThing() {
// 步骤1
userMapper.insertUser(user);
// 步骤3
userMapper.insertUserRole(user.getRole());
}
}
方法二:(新建事务)
@Service
public class UserServiceImpl implements userService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleService roleService;
@Transactional(rollbackFor = Exception.class)
@Override
public void add(User user) throws Exception {
String tableName = "user_1";
List<Attribute> columnList = new ArrayList<>();
columnList.addAll(user.getCode());
// 步骤2
userMapper.createTable(tableName,columnList);
try{
roleService.doOtherThing();
catch(Exception e){
// 查询表是否存在
int count = userMapper.selectTable(tableName);
if(count>0){
// 步骤4 删除步骤2创建的表
userMapper.dropTable(tableName);
}
// 防止异常被catch掉事务失效
// throw new RuntimeException("操作错误");
}
}
}
@Service
public class RoleServiceImpl implements RoleService{
@Autowired
private UserMapper userMapper;
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
@Override
public void doOtherThing() {
// 步骤1
userMapper.insertUser(user);
// 步骤3
userMapper.insertUserRole(user.getRole());
}
}