博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
事务的应用
阅读量:5294 次
发布时间:2019-06-14

本文共 6697 字,大约阅读时间需要 22 分钟。

1 /** 2      * 学习spring事务, 3      * 场景一:parent()  和child() 都具有事务,同时调用两个,如果不出现异常,都是可以插入到数据库中的。 4      */ 5     @Transactional 6     public void parent1(){ 7         User parent = new User("parent",19,BigDecimal.valueOf(1000)); 8         userMapper.insert(parent); 9     }10     @Transactional11     public void child1(){12         User child = new User("child",19,BigDecimal.valueOf(1000));13         userMapper.insert(child);14     }

测试:

1 @Test2     public void test05(){3         userService.parent1();4         userService.child1();5     }

是没有任何问题的。可以插入到数据库中

场景二:parent()  中使用child()

1  /** 2      * 学习spring事务, 3      * 场景二:parent()中调用child() 4      */ 5     @Transactional 6     public void parent2(){ 7         User parent = new User("parent",19,BigDecimal.valueOf(1000)); 8         userMapper.insert(parent); 9         child2();10     }11 12     /**13      * 如果当前存在事务,则挂起当前事务并且开启一个新事物继续执行,新事物执行完毕之后,14      * 然后在缓刑之前挂起的事务,如果当前不存在事务的话,则开启一个新事物。15      */16     @Transactional(propagation = Propagation.REQUIRES_NEW)17     public void child2(){18         User child = new User("child",19,BigDecimal.valueOf(1000));19         userMapper.insert(child);20     }

测试:

@Test    public void test06(){        userService.parent2();       // userService.child2();    }

也能插入,没有任何问题。

场景三:child()这个事务 中抛出了异常

1 @Transactional 2     public void parent3(){ 3         User parent = new User("parent",19,BigDecimal.valueOf(1000)); 4         userMapper.insert(parent); 5         child3(); 6     } 7     @Transactional(propagation = Propagation.REQUIRES_NEW) 8     public void child3(){ 9         User child = new User("child",19,BigDecimal.valueOf(1000));10         userMapper.insert(child);11         throw new RuntimeException("此处抛出了异常");12     }

测试:

@Test    public void test06(){        userService.parent3();    }

两个都没有插入到数据库中。

疑问1:场景C中child()抛出了异常,但是parent()没有抛出异常,按道理是不是应该parent()提交成功而child()回滚?

可能有的小伙伴要说了,child()抛出了异常在parent()没有进行捕获,造成了parent()也是抛出了异常了的!所以他们两个都会回滚!

场景四:在parent()中捕获异常

1  @Transactional 2     public void parent4(){ 3         User parent = new User("parent",19,BigDecimal.valueOf(1000)); 4         userMapper.insert(parent); 5  6         try { 7             child4(); 8         } catch (Exception e) { 9             e.printStackTrace();10         }11     }12     @Transactional(propagation = Propagation.REQUIRES_NEW)13     public void child4(){14         User child = new User("child",19,BigDecimal.valueOf(1000));15         userMapper.insert(child);16         throw new RuntimeException("此处抛出了异常");17     }

测试:

两个都插入到数据库中了。

看到这里很多小伙伴都可能会问,按照我们的逻辑来想的话child()中抛出了异常,parent()没有抛出并且捕获了child()抛出了异常!执行的结果应该是child()回滚,parent()提交成功的啊!

问题的本质。

Spring事务管理是通过JDK动态代理的方式进行实现的(另一种是使用CGLib动态代理实现的),也正是因为动态代理的特性造成了上述parent()方法调用child()方法的时候造成了child()方法中的事务失效!简单的来说,在场景四中parent()方法调用child()方法的时候,child()方法的事务是不起作用的,此时的child()方法像一个没有加事务的普通方法,其本质上就相当于下边的代码:

1 @Transactional2     public void parent3(){3         User parent = new User("parent",19,BigDecimal.valueOf(1000));4         userMapper.insert(parent);5         User child = new User("child",19,BigDecimal.valueOf(1000));6         userMapper.insert(child);7         throw new RuntimeException("此处抛出了异常");8        // child3();9     }

场景4本质:

1 @Transactional 2     public void parent4(){ 3         User parent = new User("parent",19,BigDecimal.valueOf(1000)); 4         userMapper.insert(parent); 5  6         try { 7             User child = new User("child",19,BigDecimal.valueOf(1000)); 8             userMapper.insert(child); 9             throw new RuntimeException("此处抛出了异常");10             //child4();11         } catch (Exception e) {12             e.printStackTrace();13         }14     }
View Code

因为动态代理的特性造成了场景C和场景D的本质如上述代码。在场景C中,child()抛出异常没有捕获,相当于parent事务中抛出了异常,造成parent()一起回滚,因为他们本质是同一个方法;在场景D中,child()抛出异常并进行了捕获,parent事务中没有抛出异常,parent()和child()同时在一个事务里边,所以他们都成功了;

动态代理是什么?为什么会使spring事务失效呢?

接口:

1 public interface OrderService {2     void test01();3     void test02();4 }
View Code
1 package com.demo.proxy; 2  3 public class OrderServiceImpl implements OrderService { 4     @Override 5     public void test01() { 6         System.out.println("===执行test01"); 7     } 8  9     @Override10     public void test02() {11         System.out.println("===执行test02");12     }13 }
View Code

代理类:

1 package com.demo.proxy; 2  3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 import java.lang.reflect.Proxy; 6  7 public class OrderProxy implements InvocationHandler { 8  9     private Object target;10 11     public OrderProxy(Object target) {12         this.target = target;13     }14     @Override15     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {16         if (method.getName().startsWith("test")) {17             System.out.println("===使用了动态代理");18         }19         return method.invoke(target,args);20     }21 22     public Object getProxy(){23         return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),24                 target.getClass().getInterfaces(), this);25     }26 }
View Code

测试:

1 package com.demo.proxy; 2  3 public class Test { 4     public static void main(String[] args) { 5         OrderService orderService = new OrderServiceImpl(); 6         OrderProxy orderProxy = new OrderProxy(orderService); 7          orderService = (OrderService)orderProxy.getProxy(); 8          orderService.test01(); 9          orderService.test02();10     }11 }
View Code

输出:

我们模拟一下场景C和场景D在test1()中调用test2()

1  @Override2     public void test01() {3         System.out.println("===执行test01");4         test02();5     }
View Code

输出:

根本就没有走动态代理,而是一个普通的test02()方法。

只有代理对象proxy 直接调用的那一个方法才是真正的走代理的

解决方案:

通过AopProxy上下文获取代理对象:

(1)SpringBoot配置方式:注解开启 exposeProxy = true,暴露代理对象 (否则AopContext.currentProxy()) 会抛出异常。

1 @SpringBootApplication 2 @MapperScan(value = "com.demo.mapper") 3 @EnableAspectJAutoProxy(exposeProxy = true) 4 public class DemoApplication { 5  6     public static void main(String[] args) { 7         SpringApplication.run(DemoApplication.class, args); 8     } 9 10 }
View Code

要添加jar包

org.springframework.boot
spring-boot-starter-aop
View Code

修改方法parent()

1 /** 2      * 利用AopContext 上下文获取代理对象 3      */ 4     @Transactional 5     public void parent5(){ 6         User parent = new User("parent",19,BigDecimal.valueOf(1000)); 7         userMapper.insert(parent); 8  9         try {10             ((UserServiceImpl)AopContext.currentProxy()).child4();11         } catch (Exception e) {12             e.printStackTrace();13         }14     }
View Code

这个时候,child()有异常,回滚,parent()没有异常,执行。

 

转载于:https://www.cnblogs.com/bulrush/p/10768841.html

你可能感兴趣的文章
Group Policy Settings with Silverlight
查看>>
sharepoint站点支持AJAX功能做些简要说明
查看>>
面试题32:从1到n整数中1出现的次数
查看>>
Oracle中sign/decode/nvl/round/trunc/(+)/instr/substr/replace解释
查看>>
加载声音的过程!
查看>>
重载函数
查看>>
Unity3d 引擎原理详细介绍
查看>>
Vijos p1696 数与连分数
查看>>
一个value同时满足两种interface
查看>>
连续子数组的最大和
查看>>
Luogu P1023 税收与补贴问题
查看>>
python note 32 锁
查看>>
web技术工具帖
查看>>
SpringBoot项目中常见的注解
查看>>
一次性搞明白 service和factory区别
查看>>
select下拉二级联动
查看>>
iOS UI控件5-UIPickerView
查看>>
深入Java虚拟机读书笔记第三章安全
查看>>
IO流 总结一
查看>>
素数筛选法
查看>>