springboot事物注解不⽣效_SpringBoot应⽤之事务不⽣效的⼏
种情况
⼀. 配置
将使⽤声明式事务,⾸先我们创建⼀个 SpringBoot 项⽬,版本为2.3.1.RELEASE,使⽤ mysql 作为⽬标数据库,存储引擎选择Innodb,事务隔离级别为 RR,springboot主⽅法⼊⼝开启@EnableTransactionManagement
⼆. 不⽣效
⽐如声明式事务注解@Transactional主要是结合代理实现,结合 AOP 的知识点,⾄少可以得出放在私有⽅法上,类内部调⽤都不会⽣效,下⾯进⼊详细说明
1. 数据库
事务⽣效的前提是你的数据源得⽀持事务,⽐如 mysql 的 MyISAM 引擎就不⽀持事务,⽽ Innodb ⽀持事务
下⾯的 case 都是基于 mysql + Innodb 引擎
为后续的演⽰ case,我们准备⼀些数据如下
@Servicepublic class NotEffectDemo {
@Autowired
private JdbcTemplate jdbcTemplate;
@PostConstruct
public void init() {
String sql = "replace into money (id, name, money) values" + " (520, '初始化', 200)," + "(530, '初始化', 200)," +
"(540, '初始化', 200)," + "(550, '初始化', 200)";
}
}复制代码
2. 类内部访问
简单来讲就是指⾮直接访问带注解标记的⽅法 B,⽽是通过类普通⽅法 A,然后由 A 访问 B
下⾯是⼀个简单的 case
/**
* ⾮直接调⽤,不⽣效
*
* @param id
* @return
* @throws Exception
*/@Transactional(rollbackFor = Exception.class)public boolean testCompileException2(int id) throws Exception {
if (this.updateName(id)) {
this.query("after update name", id);
if (this.update(id)) {
return true;
}
}
throw new Exception("参数异常");
}
public boolean testCall(int id) throws Exception {
return testCompileException2(id);
}复制代码
上⾯两个⽅法,直接调⽤testCompleException⽅法,事务正常操作;通过调⽤testCall间接访问,在不⽣效测试 case 如下:
@Componentpublic class NotEffectSample {
@Autowired
private NotEffectDemo notEffectDemo;
public void testNotEffect() {
testCall(530, (id) -> stCall(530));
}
private void testCall(int id, CallFunc func) {
System.out.println("============ 事务不⽣效case start ========== ");
notEffectDemo.query("transaction before", id);
try {
// 事务可以正常⼯作
func.apply(id);
} catch (Exception e) {
}
notEffectDemo.query("transaction end", id);
System.out.println("============ 事务不⽣效case end ========== \n");
}
@FunctionalInterface
public interface CallFunc {
R apply(T t) throws Exception;
}
}复制代码
输出结果如下:
============ 事务不⽣效case start ==========
transaction before >>>> {id=530, name=初始化, money=200, is_deleted=false, create_at=2020-02-03 13:44:11.0,
update_at=2020-02-03 13:44:11.0}
after update name >>>> {id=530, name=更新, money=200, is_deleted=false, create_at=2020-02-03 13:44:11.0,
update_at=2020-02-03 13:44:11.0}
transaction end >>>> {id=530, name=更新, money=210, is_deleted=false, create_at=2020-02-03 13:44:11.0,
update_at=2020-02-03 13:44:11.0}
============ 事务不⽣效case end ==========复制代码
从上⾯的输出可以看到,事务并没有回滚,主要是因为类内部调⽤,不会通过代理⽅式访问
3. 私有⽅法
在私有⽅法上,添加@Transactional注解也不会⽣效,私有⽅法外部不能访问,所以只能内部访问,上⾯的 case 不⽣效,这个当然也不⽣效了
/**
* 私有⽅法上的注解,不⽣效
*
* @param id
* @return
springboot aop* @throws Exception
*/@Transactionalprivate boolean testSpecialException(int id) throws Exception {
if (this.updateName(id)) {
this.query("after update name", id);
if (this.update(id)) {
return true;
}
}
throw new Exception("参数异常");
}复制代码
直接使⽤时,下⾯这种场景不太容易出现,因为 IDEA 会有提醒,⽂案为: Methods annotated with '@Transactional' must be overridable
4. 异常不匹配
@Transactional注解默认处理运⾏时异常,即只有抛出运⾏时异常时,才会触发事务回滚,否则并不会如
/**
* ⾮运⾏异常,且没有通过 rollbackFor 指定抛出的异常,不⽣效
*
* @param id
* @return
* @throws Exception
*/@Transactionalpublic boolean testCompleException(int id) throws Exception {
if (this.updateName(id)) {
this.query("after update name", id);
if (this.update(id)) {
return true;
}
}
throw new Exception("参数异常");
}复制代码
测试 case 如下
public void testNotEffect() {
testCall(520, (id) -> stCompleException(520));
}复制代码
输出结果如下,事务并未回滚(如果需要解决这个问题,通过设置@Transactional的 rollbackFor 属性即可)
============ 事务不⽣效case start ==========
transaction before >>>> {id=520, name=初始化, money=200, is_deleted=false, create_at=2020-02-03 13:44:11.0, update_at=2020-02-03 13:44:11.0}
after update name >>>> {id=520, name=更新, money=200, is_deleted=false, create_at=2020-02-03 13:44:11.0, update_at=2020-02-03 13:44:11.0}
transaction end >>>> {id=520, name=更新, money=210, is_deleted=false, create_at=2020-02-03 13:44:11.0, update_at=2020-02-03 13:44:11.0}
============ 事务不⽣效case end ==========复制代码
5. 多线程
这个场景可能并不多见,在标记事务的⽅法内部,另起⼦线程执⾏ db 操作,此时事务同样不会⽣效
下⾯给出两个不同的姿势,⼀个是⼦线程抛异常,主线程 ok;⼀个是⼦线程 ok,主线程抛异常
a. case1
/**
* ⼦线程抛异常,主线程⽆法捕获,导致事务不⽣效
*
* @param id
* @return
*/@Transactional(rollbackFor = Exception.class)public boolean testMultThread(int id) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
updateName(id);
query("after update name", id);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
boolean ans = update(id);
query("after update id", id);
if (!ans) {
throw new RuntimeException("failed to update ans");
}
}
}).start();
Thread.sleep(1000);
System.out.println("------- ⼦线程 --------");
return true;
}复制代码
上⾯这种场景不⽣效很好理解,⼦线程的异常不会被外部的线程捕获,testMultThread这个⽅法的调⽤不抛异常,因此不会触发事务回滚public void testNotEffect() {
testCall(540, (id) -> stMultThread(540));
}复制代码
输出结果如下
============ 事务不⽣效case start ==========
transaction before >>>> {id=540, name=初始化, money=200, is_deleted=false, create_at=2020-02-03 13:44:11.0,
update_at=2020-02-03 13:44:11.0}
after update name >>>> {id=540, name=更新, money=200, is_deleted=false, create_at=2020-02-03 13:44:11.0,
update_at=2020-02-03 13:44:11.0}
Exception in thread "Thread-3" java.lang.RuntimeException: failed to update ans
at com.git.hui.boot.jdbc.demo.NotEffectDemo$2.run(NotEffectDemo.java:112)
at java.lang.Thread.run(Thread.java:748)
after update id >>>> {id=540, name=更新, money=210, is_deleted=false, create_at=2020-02-03 13:44:11.0,
update_at=2020-02-03 13:44:11.0}
------- ⼦线程 --------
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论