1. 需求示例
需求伪代码如下:
@Service
public class JysdService {
public String test1(args) {
try {
test2(args);
} catch (Exception e) {
return "异常";
}
....
}
@Transactional(rollbackFor = Exception.class)
public Integer test2(args) {
数据库操作
}
}
如上代码,需要在方法1(test1)中调用方法2(test2),其中方法2启用事务。
2. spring中事务的注意事项
spring的声明式事务是基于代理模式的。其实代理模式相当简单, 就是将另一个类包裹在我们的类外面, 在调用我们创建的方法之前, 先经过外面的方法, 进行一些处理, 返回之前, 再进行一些操作。
2.1 声明事务的方法不能用private、static、final修饰
2.2 调用事务方法时,不能用this.xxx
原因是由于JDK的动态代理。 在SpringIoC容器中返回的调用的对象是代理对象而不是真实的对象,只有被动态代理直接调用的才会产生事务。这里的this是(JysdService)真实对象而不是代理对象,所以事务会失效,不会回滚。
同理,上面的示例代码中,在方法1中调用方法2,实际上就是this.xxx调用方式,所以这样方法2的事务会失效。
2.3 不要用try在事务方法内部处理异常,导致没有异常抛出
例如
@Transactional(rollbackFor = Exception.class)
public String test2() {
try {
数据入库
} catch (Exception e) {
return "异常";
}
return "";
}
上面这种方法事务是不会回滚的,因为你在方法内部已经把异常处理掉了,所以代理对象是捕获不到异常的,也就不会回滚。
你可以针对异常做一些处理,比如打印日志等,但是一定要把异常抛出去,例如
@Transactional(rollbackFor = Exception.class)
public String test2() throws Exception {
try {
数据入库
} catch (Exception e) {
logger.info(e.toString());
throw new Exception(); // 抛出异常
}
return "";
}
3. 针对同一个类中其他方法调用时,事务不生效的解决方案
不要在同一个类里面直接调用事务方法,即不要在service类的方法1中直接调用方法2(事务),这样是不会生效的。事务方法必须从外部调用。例如可以直接在controller中调用service中的事务方法。或者在service中创建一个当前类的外部代理对象,然后通过这个代理对象来调用当前类中的事务方法。
外部代理解决方法示例:
//解决方法
public String test1(args) {
try {
JysdService proxy =(JysdService) AopContext.currentProxy();
proxy.test2(atrs);
} catch (Exception e) {
return "异常";
}
....
}
但是我这样使用时报错了:Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available
解决方法参考(亲测有效):
JysdService jysdService = SpringUtil.getBean(this.getClass()); //SpringUtil工具类见下面代码
SpringUtil.java
@Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringUtil.applicationContext = applicationContext;
}
public static <T> T getBean(Class<T> cla) {
return applicationContext.getBean(cla);
}
public static <T> T getBean(String name, Class<T> cal) {
return applicationContext.getBean(name, cal);
}
public static Object getBean(String name){
return applicationContext.getBean(name);
}
public static String getProperty(String key) {
return applicationContext.getBean(Environment.class).getProperty(key);
}
}
参考文章
https://blog.youkuaiyun.com/mameng1988/article/details/85548812