zoukankan      html  css  js  c++  java
  • 事务的应用

     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     @Transactional
    11     public void child1(){
    12         User child = new User("child",19,BigDecimal.valueOf(1000));
    13         userMapper.insert(child);
    14     }

    测试:

    1 @Test
    2     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 @Transactional
    2     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     @Override
    10     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     @Override
    15     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  @Override
    2     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包

    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
    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()没有异常,执行。



  • 相关阅读:
    codeforces 455C 并查集
    poj 3501 Escape from Enemy Territory 预处理+二分+bfs
    POJ 2110 Mountain Walking 二分+bfs
    poj1637 Sightseeing tour 混合图欧拉回路判定
    ubuntu禁用super(win)键
    win10 ubuntu双系统安装后无法引导进入ubuntu
    python2限制函数传入的关键字参数
    python限制函数执行时间
    python classmethod 和 staticmethod的区别
    centos sendmail 启动慢
  • 原文地址:https://www.cnblogs.com/bulrush/p/10768841.html
Copyright © 2011-2022 走看看