• -手写Spring注解版本&事务传播行为


    视频参考C:UsersAdministratorDesktop蚂蚁3期【www.zxit8.com】 0018-(每特教育&每特学院&蚂蚁课堂)-3期-源码分析-手写Spring注解版本&事务传播行为018-(每特教育&每特学院&蚂蚁课堂)-3期-源码分析-手写Spring注解版本&事务传播行为

    今天视频要手写一个@Transaction事务注解框架

    我们首先来看一个spring 事务的常见配置

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/context
             http://www.springframework.org/schema/context/spring-context.xsd
             http://www.springframework.org/schema/aop
             http://www.springframework.org/schema/aop/spring-aop.xsd
             http://www.springframework.org/schema/tx
              http://www.springframework.org/schema/tx/spring-tx.xsd">
    
    
        <!-- 开启注解 -->
        <context:component-scan base-package="com.itmayiedu"></context:component-scan>
        <!-- 1. 数据源对象: C3P0连接池 -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
            <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
            <property name="user" value="root"></property>
            <property name="password" value="root"></property>
        </bean>
    
        <!-- 2. JdbcTemplate工具类实例 -->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!-- 配置事物 -->
        <bean id="dataSourceTransactionManager"
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
        <!-- 开启注解事物 -->
        <tx:annotation-driven transaction-manager="dataSourceTransactionManager" />
    </beans>

     在业务层中@Transaction来使用注解

        @Transactional
        public void add() {
            userDao.add("wangmazi", 27);
            int i = 1 / 0;
            System.out.println("我是add方法");
            userDao.add("zhangsan", 16);
        }

    接下来我们要手写@Transactional注解框架

    接下来首先学习下java的注解以及利用反射读取注解

    @Target(value = { ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AddAnnotation {
    
        int userId() default 0;
    
        String userName() default "默认名称";
    
        String[]arrays();
    }

     接下来利用反射读取注解

    package com.itmayiedu.annotation;
    
    import java.lang.reflect.Method;
    
    public class User {
    
        @AddAnnotation(userName = "张三", userId = 18, arrays = { "1" })
        public void add() {
    
        }
    
        public void del() {
    
        }
    
        public static void main(String[] args) throws ClassNotFoundException {
            // 怎么样获取到方法上注解信息 反射机制
            Class<?> forName = Class.forName("com.itmayiedu.annotation.User");
            // 获取到当前类(不包含继承)所有的方法
            Method[] declaredMethods = forName.getDeclaredMethods();
            for (Method method : declaredMethods) {
                // 获取该方法上是否存在注解
                System.out.println("####方法名称" + method.getName());
                AddAnnotation addAnnotation = method.getAnnotation(AddAnnotation.class);
                if (addAnnotation == null) {
                    // 该方法上没有注解
                    System.out.println("该方法上没有加注解..");
                    continue;
                }
                // 在该方法上查找到该注解
                System.out.println("userId:" + addAnnotation.userId());
                System.out.println("userName:" + addAnnotation.userName());
                System.out.println("arrays:" + addAnnotation.arrays());
                System.out.println();
            }
        }
    
    }

    现在有个User类,类中add方法我们添加了注解,接下来使用反射来操作User类,通过反射获得add方法,然后判断该方法上面是否使用了注解,如果使用了把注解的值打印出来

    我们来看下打印的日志

    ####方法名称main
    该方法上没有加注解..
    ####方法名称add
    userId:18
    userName:张三
    arrays:[Ljava.lang.String;@6607db7d
    
    ####方法名称del
    该方法上没有加注解..

    接下来我们重点讲解手写@Transaction spring的事物注解

    例如现在add方法上我们使用@ExtTransactional如何实现注解了。定义一个aop的切面类,在切面类中定义切点可以拦截到controller类的add方法被调用了。在切面中利用反射技术判断add方法上面是否使用了注解,如果使用了注解就开启事务,没有就不开启事务

    第一步:需要将spring框架配置文件中的开启事务管理注解去掉

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/context
             http://www.springframework.org/schema/context/spring-context.xsd
             http://www.springframework.org/schema/aop
             http://www.springframework.org/schema/aop/spring-aop.xsd
             http://www.springframework.org/schema/tx
              http://www.springframework.org/schema/tx/spring-tx.xsd">
    
        <context:component-scan base-package="com.itmayiedu"></context:component-scan>
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <!-- 开启事物注解 -->
    
        <!-- 1. 数据源对象: C3P0连接池 -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
            <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
            <property name="user" value="root"></property>
            <property name="password" value="root"></property>
        </bean>
    
        <!-- 2. JdbcTemplate工具类实例 -->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!-- 3.配置事务 -->
        <bean id="dataSourceTransactionManager"
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
    </beans>

     去掉:

        <!-- 开启注解事务 -->
        <tx:annotation-driven transaction-manager="dataSourceTransactionManager" />
    第二步:自定义一个注解
    package com.itmayiedu.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    // 事务注解 设置传播行为
    @Target({ ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ExtTransaction {
    
    }

    第三步:自定义一个事务管理工具的实现类,这里使用的是jdbc的事务管理器来实现对事务的管理

    package com.itmayiedu.transaction;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Scope;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    import org.springframework.stereotype.Component;
    import org.springframework.transaction.TransactionStatus;
    import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
    
    // 编程事务(需要手动begin 手动回滚 手都提交)
    @Component
    @Scope("prototype") // 每个事务都是新的实例 目的解决线程安全问题 多例子
    public class TransactionUtils {
    
        // 全局接受事务状态
        private TransactionStatus transactionStatus;
        // 获取事务源
        @Autowired
        private DataSourceTransactionManager dataSourceTransactionManager;
    
        // 开启事务
        public TransactionStatus begin() {
            System.out.println("开启事务");
            transactionStatus = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
            return transactionStatus;
        }
    
        // 提交事务
        public void commit(TransactionStatus transaction) {
            System.out.println("提交事务");
            dataSourceTransactionManager.commit(transaction);
        }
    
        // 回滚事务
        public void rollback() {
            System.out.println("回滚事务...");
            dataSourceTransactionManager.rollback(transactionStatus);
        }
    
    }

    上面这个类有几个地方需要注意的,dao层使用的是jdbcTemplate,使用jdbc的方式,所以要使用jbdc数据源的事务管理器

    DataSourceTransactionManager,其他框架hibernate的事务管理器为

    Spring声明式事务管理器类:
    Jdbc技术:DataSourceTransactionManager
    Hibernate技术:HibernateTransactionManager

    第二:对于事务管理工具类一个事务应该对于一个事务管理工具类的实例,spring默认代理对象都是单例模式,如果是单例模式多个事务就是多个线程共享一个对象,会出现线程安全问题

    所以@Scope("prototype") // 每个事务都是新的实例 目的解决线程安全问题 多例子,定义为多实例类型

    接下来,定位aop实现对调用方法的拦截,实现事务管理

    package com.itmayiedu.aop;
    
    import java.lang.reflect.Method;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.transaction.TransactionStatus;
    import org.springframework.transaction.interceptor.TransactionAspectSupport;
    
    import com.itmayiedu.annotation.ExtTransaction;
    import com.itmayiedu.transaction.TransactionUtils;
    
    //  自定义事务注解具体实现
    @Aspect
    @Component
    public class AopExtTransaction {
        // 一个事务实例子 针对一个事务
        @Autowired
        private TransactionUtils transactionUtils;
    
        // 使用异常通知进行 回滚事务
        @AfterThrowing("execution(* com.itmayiedu.service.*.*.*(..))")
        public void afterThrowing() {
            // 获取当前事务进行回滚
            // TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            transactionUtils.rollback();
        }
    
        // 环绕通知 在方法之前和之后处理事情
        @Around("execution(* com.itmayiedu.service.*.*.*(..))")
        public void around(ProceedingJoinPoint pjp) throws Throwable {
    
            // 1.获取该方法上是否加上注解
            ExtTransaction extTransaction = getMethodExtTransaction(pjp);
            TransactionStatus transactionStatus = begin(extTransaction);
            // 2.调用目标代理对象方法
            pjp.proceed();
            // 3.判断该方法上是否就上注解
            commit(transactionStatus);
        }
    
        private TransactionStatus begin(ExtTransaction extTransaction) {
            if (extTransaction == null) {
                return null;
            }
            // 2.如果存在事务注解,开启事务
            return transactionUtils.begin();
        }
    
        private void commit(TransactionStatus transactionStatus) {
            if (transactionStatus != null) {
                // 5.如果存在注解,提交事务
                transactionUtils.commit(transactionStatus);
            }
    
        }
    
        // 获取方法上是否存在事务注解
        private ExtTransaction getMethodExtTransaction(ProceedingJoinPoint pjp)
                throws NoSuchMethodException, SecurityException {
            String methodName = pjp.getSignature().getName();
            // 获取目标对象
            Class<?> classTarget = pjp.getTarget().getClass();
            // 获取目标对象类型
            Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes();
            // 获取目标对象方法
            Method objMethod = classTarget.getMethod(methodName, par);
            ExtTransaction extTransaction = objMethod.getAnnotation(ExtTransaction.class);
            return extTransaction;
        }
    
    }
    package com.itmayiedu.dao;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class UserDao {
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        public void add(String name, Integer age) {
            String sql = "INSERT INTO t_users(NAME, age) VALUES(?,?);";
            int updateResult = jdbcTemplate.update(sql, name, age);
            System.out.println("updateResult:" + updateResult);
        }
    
    }
    package com.itmayiedu.service;
    
    //user 服务层
    public interface UserService {
    
        public void add();
    
        public void del();
    }

    接下来在实现类使用

     @ExtTransaction就实现了我们的自定义事务注解
    package com.itmayiedu.service.impl;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.itmayiedu.dao.UserDao;
    import com.itmayiedu.service.LogService;
    import com.itmayiedu.service.UserService;
    
    //user 服务层
    @Service
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserDao userDao;
    @Autowired
        private TransactionUtils transactionUtils;
    
      
        @ExtTransactionpublic void add() {
            userDao.add("test001", 20);
            // int i = 1 / 0;
            System.out.println("################");
            userDao.add("test002", 21);
    
        }
        // 方法执行完毕之后,才会提交事务
    
        public void del() {
            System.out.println("del");
        }
    
    }

     接下来我们分析下事务的传播特性,我们使用spring框架默认自带的注解

    我们在配置文件中开启事务管理,然后将上面我们自己编写的AopExtTransaction事务框架代码屏蔽掉,以免和spring默认的框架起冲突

    注意:spring的事务传播特性是在两个不同的service中相互调用

    我们在新建一个dao

    package com.itmayiedu.dao;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class LogDao {
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        public void add(String name) {
            String sql = "INSERT INTO t_log(log_name) VALUES(?);";
            int updateResult = jdbcTemplate.update(sql, name);
            System.out.println("##LogDao##updateResult:" + updateResult);
        }
    
    }
    package com.itmayiedu.service.impl;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.itmayiedu.dao.LogDao;
    import com.itmayiedu.service.LogService;
    
    @Service
    public class LogServiceImpl implements LogService {
        @Autowired
        private LogDao logDao;
    
        @Transactional
        public void addLog() {
            logDao.add("addLog" + System.currentTimeMillis());
            // int i = 1 / 0;
        }
    
    }
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/context
             http://www.springframework.org/schema/context/spring-context.xsd
             http://www.springframework.org/schema/aop
             http://www.springframework.org/schema/aop/spring-aop.xsd
             http://www.springframework.org/schema/tx
              http://www.springframework.org/schema/tx/spring-tx.xsd">
    
        <context:component-scan base-package="com.itmayiedu"></context:component-scan>
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <!-- 开启事物注解 -->
    
        <!-- 1. 数据源对象: C3P0连接池 -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
            <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
            <property name="user" value="root"></property>
            <property name="password" value="root"></property>
        </bean>
    
        <!-- 2. JdbcTemplate工具类实例 -->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!-- 3.配置事务 -->
        <bean id="dataSourceTransactionManager"
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
        <!-- 开启注解事务 -->
        <tx:annotation-driven transaction-manager="dataSourceTransactionManager" />
    </beans>
     使用spring框架的默认事务管理,这里需要开启注解事务管理的配置

    <!-- 开启注解事务 -->
    <tx:annotation-driven transaction-manager="dataSourceTransactionManager" />

    PROPAGATION_REQUIRED—如果当前有事务,就用当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。

    对应的就是在addLog()方法上面使用@Transactional注解

     

    @Transactional
    public void addLog() {
    logDao.add("addLog" + System.currentTimeMillis());

    }

    @Transactional
    public void add() {
    // 调用接口的时候 接口失败 需要回滚,但是日志记录不需要回滚。
    logService.addLog(); // 后面程序发生错误,不能影响到我的回滚### 正常当addLog方法执行完毕,就应该提交事务
    userDao.add("test001", 200);
    // int i = 1 / 0;
    System.out.println("################");
    userDao.add("test002", 21);

    }

    我们来看下运行的情况

    第一种情况:正常情况下,事务正常 log日志和人员信息都会添加成功

    第二种情况,如果在addLog方法中发送了异常

    @Transactional
    public void addLog() {
    logDao.add("addLog" + System.currentTimeMillis());
    int i = 1 / 0;
    }

    这个时候因为addLog中发送了异常,这个时候就会把异常抛出去给aop框架处理,后续的代码就不会再继续执行

    就不会再执行

    logService.addLog(); // 后面程序发生错误,不能影响到我的回滚### 正常当addLog方法执行完毕,就应该提交事务
    userDao.add("test001", 200);
    // int i = 1 / 0;
    System.out.println("################");
    userDao.add("test002", 21);

    因为addLOg方法出现了异常就不会再执行后续的userDao.add("test001", 200);方法,抛出异常的时候aop框架会对事务进行回滚,所以ogDao.add("addLog" + System.currentTimeMillis());不会添加数据到数据库中

    第三种情况

    如果在addLog方法中发送了异常

    @Transactional
    public void addLog() {
    logDao.add("addLog" + System.currentTimeMillis());
    int i = 1 / 0;
    }

    但是在userService的add方法中使用了try catch进行捕获,我们来看下数据库的情况

    @Transactional(rollbackFor=Exception.class)
        public void add() {
            // 调用接口的时候 接口失败 需要回滚,但是日志记录不需要回滚。
            
            try{
                logService.addLog(); 
            }catch(Exception e){
                System.out.println(e.toString());
            }
            
            userDao.add("test001", 200);
            // int i = 1 / 0;
            System.out.println("################");
            userDao.add("test002", 21);
    
        }

    我们来看下这种情况下的运行情况

    运行居然抛出了异常

    Exception in thread "main" org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:717)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at com.sun.proxy.$Proxy13.add(Unknown Source)
    at com.itmayiedu.Test001.main(Test001.java:12)

    数据库中没有插入任何数据,为啥出现上面这种情况了

    ##Transaction rolled back because it has been marked as rollback-only spring 具备多种事务传播机制,最常用的是REQUIRED,即如果不存在事务,则新建一个事务;如果存在事务,则加入现存的事务中。 示例代码如下:

    public void A() {
    querySomething(...);
    try {
    B()
    } catch () {
    }
    saveSomethinf();
    }

    public void B() {
    throw Exception()
    }
    此时B会和A存在一个事务中。如果B抛出异常没有捕获,即使在A中捕获并处理,仍会发生异常:Transaction rolled back because it has been marked as rollback-only 因为spring会在A捕获异常之前提前捕获到异常,并将当前事务设置为rollback-only,而A觉得对异常进行了捕获,它仍然继续commit,当TransactionManager发现状态为设置为rollback-only时, 则会抛出UnexpectedRollbackException 相关代码在AbstractPlatformTransactonManager.java中:

    public final void commit(TransactionStatus status) throws TransactionException {
    if (status.isCompleted()) {
    throw new IllegalTransactionStateException(
    "Transaction is already completed - do not call commit or rollback more than once per transaction");
    }

    DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    if (defStatus.isLocalRollbackOnly()) {
    if (defStatus.isDebug()) {
    logger.debug("Transactional code has requested rollback");
    }
    processRollback(defStatus);
    return;
    }
    if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
    if (defStatus.isDebug()) {
    logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
    }
    processRollback(defStatus);
    // Throw UnexpectedRollbackException only at outermost transaction boundary
    // or if explicitly asked to.
    if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
    throw new UnexpectedRollbackException(
    "Transaction rolled back because it has been marked as rollback-only");
    }
    return;
    }

    processCommit(defStatus);
    }
    解决方法: 在抛出异常的最原始地方处理异常,即在spring捕获到异常之前处理掉

    在一个transactional中如果有另一transaction,内层的事务会跟随外层的事务,变为一个整体的事务。

    如果内层transaction发生了异常,即使你捕捉了这个异常,那么整体的Transaction也会被定义成RollbackOnly,这也正是事务管理的原则。所以当外层事务想提交整个事务时,出现异常。

    解决版本就是在logService中对异常进行try catch处理

    @Transactional
        public void addLog() {
            try{
                logDao.add("addLog" + System.currentTimeMillis());
                int i = 1 / 0;
            }catch(Exception e){
                
            }
    
        }
    @Transactional(rollbackFor=Exception.class)
        public void add() {
            // 调用接口的时候 接口失败 需要回滚,但是日志记录不需要回滚。
            
                logService.addLog(); 
    
            
            userDao.add("test001", 200);
            // int i = 1 / 0;
            System.out.println("################");
            userDao.add("test002", 21);
    
        }

    上面这种情况:log日志和人员信息都会添加到数据库中, int i = 1 / 0;发现了异常但是我们自定义了try catch处理异常,就不会把异常抛出去给aop框架进行处理,对数据进行回滚

    因为logService.addLog(); 对外没有抛出异常,后续的 userDao.add("test001", 200);代码也会正常执行,把人员数据添加到数据库中

    第五情况在add添加人员方法中抛出了异常

        @Transactional
        public void addLog() {
                logDao.add("addLog" + System.currentTimeMillis());
                int i = 1 / 0;
    
    
        }
        @Transactional(rollbackFor=Exception.class)
        public void add() {
            // 调用接口的时候 接口失败 需要回滚,但是日志记录不需要回滚。
            
                logService.addLog(); 
            userDao.add("test001", 200);
            int i = 1 / 0;
            System.out.println("################");
            userDao.add("test002", 21);
    
        }

    在调用add方法中抛出了异常,因为当前事务addLog的事务传播属性是   @Transactional,PROPAGATION_REQUIRED—如果当前有事务,就用当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。

    所以logService.addLog();和userDao.add("test001", 200);是在同一个事务中,当发现异常的时候,如果没有对异常使用try catch进行处理,会把异常抛出去给aop框架进行处理

    异常前面的

     logService.addLog(); 和 userDao.add("test001", 200);都会回滚


  • 相关阅读:
    MongoDB数据类型
    Redis数据类型
    RHEL7 CentOS7 检查查看精简指令
    Linux命令:查看登录用户
    JavaScript错误之:Uncaught ReferenceError: $ is not defined
    Linux下因为系统编码问题造成乱码的解决办法
    Linux系统下的程序开发之:命名规范
    优化php代码
    Git工具:Widows下的使用(提交到Github)
    MongoDB
  • 原文地址:https://www.cnblogs.com/kebibuluan/p/10708104.html
Copyright © 2020-2023  润新知