事务注解@Transactional不起作用原因及解决办法

参考
https://blog.csdn.net/qq_20597727/article/details/84900994
https://www.cnblogs.com/chen-lhx/p/6956152.html

Transactional失效场景介绍

第一种

Transactional注解标注方法修饰符为非public时,注解不起作用.

1
2
3
4
5
6
7
8
9
10
@Component
public class TestServiceImpl {
@Resource
TestMapper testMapper;

@Transactional
void insertTestWrongModifier() {
testMapper.insert(new Test(10,20,30));
}
}

第二种

@Transactional标注方法所在类中调用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component
public class TestServiceImpl implements TestService {
@Resource
TestMapper testMapper;

@Transactional
public void insertTestInnerInvoke() {
//正常public修饰符的事务方法
testMapper.insert(new Test(10,20,30));
}

public void testInnerInvoke(){
//类内部调用@Transactional标注的方法。
insertTestInnerInvoke();
}
}

第三种

捕获了异常,但不能回滚事务.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
public class TestServiceImpl {
@Resource
TestMapper testMapper;

@Transactional
void insertTestWrongModifier() {
try {
int num = testMapper.insert(new Test(10,20,30));
if (num > 0){
throw new NeedToInterceptException("need intercept");
}
} catch (Exception e) {
System.out.println("i catch exception");
}
}
}

解决办法

第一种

@Transactional标注的方法必须是public才生效.

1
2
3
4
5
6
7
8
protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
// Don't allow no-public methods as required.
// 非public 方法,返回@Transactional信息一律是null
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
...
}

第二种

要想在内部使用,使用非this调用.
因为事务管理是基于动态代理对象实现的,如果在类内部自己调用其方法,就不是动态代理对象了,而是使用的this对象,从而绕过了动态代理,使其无效.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class TestServiceImpl implements TestService {
@Resource
TestMapper testMapper;

@Resource
TestServiceImpl testServiceImpl;

@Transactional
public void insertTestInnerInvoke() {
//正常public修饰符的事务方法
testMapper.insert(new Test(10,20,30));
}

public void testInnerInvoke(){
//类内部调用@Transactional标注的方法。
testServiceImpl.insertTestInnerInvoke();
}
}

第三种

在catch块中抛异常,或在catch块中增加TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
public class TestServiceImpl {
@Resource
TestMapper testMapper;

@Transactional
void insertTestWrongModifier() {
try {
int num = testMapper.insert(new Test(10,20,30));
if (num > 0){
throw new NeedToInterceptException("need intercept");
}
} catch (Exception e) {
System.out.println("i catch exception");
throw new RuntimeException();
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
public class TestServiceImpl {
@Resource
TestMapper testMapper;

@Transactional
void insertTestWrongModifier() {
try {
int num = testMapper.insert(new Test(10,20,30));
if (num > 0){
throw new NeedToInterceptException("need intercept");
}
} catch (Exception e) {
System.out.println("i catch exception");
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
}