精华内容
下载资源
问答
  • @Transactional调用失效问题 @Transactional在某些场景下会失效,我们把传播行为修改为REQUIRESNEW,也就是每次调用产生新的事务 @Transactional(isolation = Isolaton.READCOMMITTED, propagation = Propagation....

    @Transactional自调用失效问题

    @Transactional在某些场景下会失效,我们把传播行为修改为REQUIRESNEW,也就是每次调用产生新的事务

    @Transactional(isolation = Isolaton.READCOMMITTED, propagation = Propagation.REQUIRED) 
    public int insertUsers (List<User> userList) { 
    	int count = 0; 
    	for (User user : userList) 
    	{ 
    		//调用自己类自身的方法,产生自调用问题
    		count += insertUser(user), 
    	}
    	return count; 
    }
    	//传播行为为REQUIRESNEW每次调用产生新事务
    @Override 
    @Transactional(isolation =Isolation.READ COMMITTED, propagation = PropagatonREQUIRESNEW) 
    publicntinsertUser (User user) { 
    	return userDao.insertUser(user); 
    }
    

    这是一个类自身方法之间的调用,我称之为自调用.
    pring数据库事务约定其实原理是AOP,AOP的原理是在自调用的过程中是类自身的调用,而不是理对象去调用,那么就不会产生AOP,这样Spring就不能把你的代码织入到约定的流程中于是就产生了现在看到的失败场景。为了克服这个问题,需要用一个Service去调用另一Service这样就是将代理对象的调用,Spring会将你的代码织入事务流程。

    或者改造代码:

    @Au tow ired prvateUserDao userDao = null; 
    private ApplicatonContextapplicationContext = null; 
    //实现生命周期方法,设置IoC容器@Oerride
    public void setApplicationContext(ApplicationContext  applicationContext)  throws BeansException {
    	this.applicationContext = applicationContext; 
    }
    
    @Transactional(isolation = Isolaton.READCOMMITTED, propagation = Propagation.REQUIRED) 
    public int insertUsers (List<User> userList) { 
    	int count = 0;
    	//从IOC容器中取代理对象
    	UserService userService = applicationContext.getBean(UserService.class);  
    	for (User user : userList) 
    	{ 
    		//调用自己类自身的方法,产生自调用问题
    		count += userService .insertUser(user), 
    	}
    	return count; 
    }
    	//传播行为为REQUIRESNEW每次调用产生新事务
    @Override 
    @Transactional(isolation =Isolation.READ COMMITTED, propagation = PropagatonREQUIRESNEW) 
    publicntinsertUser (User user) { 
    	return userDao.insertUser(user); 
    }
    

    从代码中我实现了ApplicationContextAware 接口setApplicationContext方法这样便够把IoC容器设置到这个类中来。于是在setUser方法中,我通过Io容器取了UserService口对象。但是请注意这将是一个理对象并且使用传播行为为REUIRES_NEW的insertUse方法,这样才可以运行功。
    这样自调用的问题就克服了,只是这样代码需要依赖于Spring的API,这样会造成代码的侵入。

    展开全文
  • @Transactional调用失效问题解析

    千次阅读 2019-06-26 00:12:21
    一、背景 ”脏脏包“在技术群里问了一个问题:”大家有在项目中遇到这样的场景吗 在一个service层重写的方法中调用一个私有方法。 service重写的方法不加事务 私有...在应用调用声明@Transactional 的目标方法时...

    一、背景

    ”脏脏包“在技术群里问了一个问题:”大家有在项目中遇到这样的场景吗 在一个service层重写的方法中调用一个私有方法。 service重写的方法不加事务 私有方法想加入事务 他去调用私有方法时 私有方法需要被事务控制“ 。

    这个问题比较典型,面试时也经常被问到,在此简单整理一下。

    二、Spring注解方式的事务实现机制

    在应用调用声明@Transactional 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据@Transactional 的属性配置信息,这个代理对象决定该声明@Transactional 的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor 拦截时,会在在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器(图 2 有相关介绍)AbstractPlatformTransactionManager 操作数据源 DataSource 提交或回滚事务, 如图 1 所示。

     

    图 1. Spring 事务实现机制

    Spring AOP 代理有 CglibAopProxy 和 JdkDynamicAopProxy 两种,图 1 是以 CglibAopProxy 为例,对于 CglibAopProxy,需要调用其内部类的 DynamicAdvisedInterceptor 的 intercept 方法。

    对于 JdkDynamicAopProxy,需要调用其 invoke 方法。

     

     

    三、分析

    3.1 为什么@Transactional 只能应用到 public 方法才有效?

    3.1.1 从理论角度

    理论上@Transactional 注解是为了进行事务增强。

    JDK 动态代理,比如需事先接口才行,因此必然是 public的。

    AspectJ 动态代理,是基于类的代理,如果该类的方法为私有,那么子类中就无法重写和调用。

     

    3.1.2 从源码角度

    这是因为在使用 Spring AOP 代理时,Spring 在调用在的 TransactionInterceptor 在目标方法执行前后进行拦截之前,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource(Spring 通过这个类获取 @Transactional 注解的事务属性配置属性信息)的 computeTransactionAttribute 方法。

    其源码如下:

    private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
    // 关键在这里
                if (this.allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
                    return null;
                } else {
                    Class<?> userClass = ProxyUtils.getUserClass(targetClass);
                    Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
                    specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
                    TransactionAttribute txAtt = null;
                    if (specificMethod != method) {
                        txAtt = this.findTransactionAttribute(method);
                        if (txAtt != null) {
                            return txAtt;
                        }
    
                        txAtt = this.findTransactionAttribute(method.getDeclaringClass());
                        if (txAtt != null || !this.enableDefaultTransactions) {
                            return txAtt;
                        }
                    }
    
                    txAtt = this.findTransactionAttribute(specificMethod);
                    if (txAtt != null) {
                        return txAtt;
                    } else {
                        txAtt = this.findTransactionAttribute(specificMethod.getDeclaringClass());
                        if (txAtt != null) {
                            return txAtt;
                        } else if (!this.enableDefaultTransactions) {
                            return null;
                        } else {
                            Method targetClassMethod = this.repositoryInformation.getTargetClassMethod(method);
                            if (targetClassMethod.equals(method)) {
                                return null;
                            } else {
                                txAtt = this.findTransactionAttribute(targetClassMethod);
                                if (txAtt != null) {
                                    return txAtt;
                                } else {
                                    txAtt = this.findTransactionAttribute(targetClassMethod.getDeclaringClass());
                                    return txAtt != null ? txAtt : null;
                                }
                            }
                        }
                    }
                }
            }

    非公有函数事务属性信息返回null

     

     

    3.2 为什么自调用无效?

    在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理,这会造成自调用问题。

    若同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,有@Transactional 注解的方法的事务被忽略,不会发生回滚。

    @Service
    public class OrderService {
    
        private void insert() {
    insertOrder();
    }
    
    @Transactional
        public void insertOrder() {
            //SQL操作
           }
    }

    insertOrder 尽管有@Transactional 注解,但它被内部方法 insert 调用,事务被忽略,出现异常事务不会发生回滚。

     

    四、解决方法

    4.1 可以使用ApplicatonContextHolder 工具类,从上下文中获取当前bean,再调用。

    4.2 可以使用上下文工具类获取当前对象的代理类  @EnableAspectJAutoProxy (exposeProxy = true) 然后通过下面方法获取代理对象,然后再调用

    
    
    @Service
    public class OrderService {
    
      public void insert() {
    
        OrderService proxy = (OrderService) AopContext.currentProxy();
           proxy.insertOrder();
        }
    
        @Transactional
        public void insertOrder() {
            //SQL操作
           }
    }

    4.3  maven中加入spring-aspects 和 aspectjrt 的依赖以及 aspectj-maven-plugin插件

     

    注:

    第二节和第三节部分内容转载自:

     透彻的掌握 Spring 中@transactional 的使用

    创作不易,如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。

    展开全文
  • 须知 @Transactional 底层使用TransactionInterceptor类在方法执行前后开启事务和关闭事务 ...接口和实现类,test1中不加@Transactional,test2加@Transactional,外部调用test1()方法 结果:test2方法失败时,事务并

    须知

    @Transactional 底层使用TransactionInterceptor类在方法执行前后开启事务和关闭事务

    spring的动态代理,分为两种:jdk自身代理和cglib代理方式

    参考:https://blog.csdn.net/a837199685/article/details/68930987

    objenesis参考:http://objenesis.org/index.html  主要用于不用构造方法初始化实例

    场景

    case1

    接口和实现类,test1中不加@Transactional,test2加@Transactional,外部调用test1()方法

    结果:test2方法失败时,事务并不会回滚

    //接口
    public interface CeshiService {
        void test1();
    }
    
    
    //test1方法上没有Transactional注解,test2方法上有Transactional注解
    @Service
    public class CeshiServiceImpl implements CeshiService{
    
        public void test1(){
            test2();
        }
    
        @Transactional(rollbackFor = Exception.class)
        public void test2() {
            Db.update("INSERT INTO \"public\".\"ceshi\"(\"id\", \"name\") VALUES (2, '2');");
            throw new Exception("异常");
        }
    }
    

    分析:

    debug发下,CeshiServiceImpl中的this指向的是CeshiServiceImpl@11343 实例,并不是$.Proxy代理对象,那么当也就决定了内部调用test2时,不会走动态代理的链路,那么事务也就不会生效

    case2

    普通类,test1中不加@Transactional,test2加@Transactional,外部调用test1()方法

    结果:test2方法失败时,事务并不会回滚

    //test1方法上没有Transactional注解,test2方法上有Transactional注解
    @Service
    public class CeshiService{
    
        public void test1(){
            test2();
        }
    
        @Transactional(rollbackFor = Exception.class)
        public void test2() {
            Db.update("INSERT INTO \"public\".\"ceshi\"(\"id\", \"name\") VALUES (2, '2');");
            throw new Exception("异常");
        }
    }
    

    分析:

    debug发下,CeshiServiceImpl中的this指向的是CeshiServiceImpl@11342 实例,并不是Enhanced代理对象,那么当也就决定了内部调用test2时,不会走动态代理的链路,那么事务也就不会生效

    如何生效

    1. 首先开启exposeProxy,然后((CeshiService) AopContext.currentProxy()).test2();去调用test2内部方法

    @Transactional 代码分析

    TransactionInterceptor类

    public Object invoke(final MethodInvocation invocation) throws Throwable {
            Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
            //主要来此处的invokeWithinTransaction方法,把真正的方法包围了
            return this.invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
                public Object proceedWithInvocation() throws Throwable {
                    return invocation.proceed();
                }
            });
    }
    
    

    TransactionAspectSupport中的invokeWithinTransaction方法

    protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
    			throws Throwable {
    
    		// If the transaction attribute is null, the method is non-transactional.
            //获取注解属性
    		final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
    		final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    
    		if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
    			// Standard transaction demarcation with getTransaction and commit/rollback calls.
                //开启事务
    			TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
    			Object retVal = null;
    			try {
    				// This is an around advice: Invoke the next interceptor in the chain.
    				// This will normally result in a target object being invoked.
                    //继续执行下一个链路
    				retVal = invocation.proceedWithInvocation();
    			}
    			catch (Throwable ex) {
    				// target invocation exception
                    //里面执行事务的提交或回滚
    				completeTransactionAfterThrowing(txInfo, ex);
    				throw ex;
    			}
    			finally {
    				cleanupTransactionInfo(txInfo);
    			}
    			commitTransactionAfterReturning(txInfo);
    			return retVal;
    		}
    
    		else {
    			// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
    			try {
    				Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
    						new TransactionCallback<Object>() {
    							@Override
    							public Object doInTransaction(TransactionStatus status) {
    								TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
    								try {
    									return invocation.proceedWithInvocation();
    								}
    								catch (Throwable ex) {
    									if (txAttr.rollbackOn(ex)) {
    										// A RuntimeException: will lead to a rollback.
    										if (ex instanceof RuntimeException) {
    											throw (RuntimeException) ex;
    										}
    										else {
    											throw new ThrowableHolderException(ex);
    										}
    									}
    									else {
    										// A normal return value: will lead to a commit.
    										return new ThrowableHolder(ex);
    									}
    								}
    								finally {
    									cleanupTransactionInfo(txInfo);
    								}
    							}
    						});
    
    				// Check result: It might indicate a Throwable to rethrow.
    				if (result instanceof ThrowableHolder) {
    					throw ((ThrowableHolder) result).getThrowable();
    				}
    				else {
    					return result;
    				}
    			}
    			catch (ThrowableHolderException ex) {
    				throw ex.getCause();
    			}
    		}
    	}
    

     

    protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
    		if (txInfo != null && txInfo.hasTransaction()) {
    			if (logger.isTraceEnabled()) {
    				logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
    						"] after exception: " + ex);
    			}
                // 注解上指定的回滚异常类型是否匹配
    			if (txInfo.transactionAttribute.rollbackOn(ex)) {
    				try {
                 //回滚					txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
    				}
    				catch (TransactionSystemException ex2) {
    					logger.error("Application exception overridden by rollback exception", ex);
    					ex2.initApplicationException(ex);
    					throw ex2;
    				}
    				catch (RuntimeException ex2) {
    					logger.error("Application exception overridden by rollback exception", ex);
    					throw ex2;
    				}
    				catch (Error err) {
    					logger.error("Application exception overridden by rollback error", ex);
    					throw err;
    				}
    			}
    			else {
    				// We don't roll back on this exception.
    				// Will still roll back if TransactionStatus.isRollbackOnly() is true.
    				try {
    //提交事务					txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
    				}
    				catch (TransactionSystemException ex2) {
    					logger.error("Application exception overridden by commit exception", ex);
    					ex2.initApplicationException(ex);
    					throw ex2;
    				}
    				catch (RuntimeException ex2) {
    					logger.error("Application exception overridden by commit exception", ex);
    					throw ex2;
    				}
    				catch (Error err) {
    					logger.error("Application exception overridden by commit error", ex);
    					throw err;
    				}
    			}
    		}
    	}

    rollbackOn方法,从此可以看到,默认只要当是RuntimeException或Error异常时才会回滚,非RuntimeException的Exception默认不会回滚

    	@Override
    	public boolean rollbackOn(Throwable ex) {
    		return (ex instanceof RuntimeException || ex instanceof Error);
    	}
    

    总结

    我们发现不论是jdk动态代理还是cglib动态代理,执行类方法时,this对象始终是原始实例,也就决定了,内部调用时,不会触发动态代理里面的方法。

     

     

    展开全文
  • 情况一:加@Transactional的注解方法A ,调用了本类的都加@Transactional注解的方法B和C。此种情况注解生效,报错回滚! 情况二:把A的注解去掉。此种情况注解不生效,没有回滚! 测试:(加日志,看console的输出...

    背景:

    情况一:加@Transactional的注解方法A ,调用了本类的都加@Transactional注解的方法B和C。此种情况注解生效,报错回滚!

    情况二:把A的注解去掉。此种情况注解不生效,没有回滚!

    测试:(加日志,看console的输出,就可以知道有没有事务创建)

    加配置

    logback-spring.xml 

    <logger name="org.springframework.jdbc.datasource.DataSourceTransactionManager" level="DEBUG" /> 走此类的方法都输出日志

    application-dev.yml

    logging:

      config: classpath:logback-spring.xml

    情况一的时候,创建了一个事务,B和C的方法都没有创建事务。

    情况二的时候,没有创建事务,验证了内部调用注解不生效的问题。

    另,网上文档@Transational不生效还有几种情况,1 private修辞的方法不生效 2 方法中有try catch不生效,要有显示的异常才会被spring捕捉到,在catch中 throw new RunTimeException 或者setrollbackonly();

     

    展开全文
  • springboot @Transactional调用失效问题

    千次阅读 2018-08-24 10:29:56
    spring的数据库事务约定的实现原理是AOP,而AOP的原理是动态代理,在自调用的过程中,是类自身调用,而不是代理对象去调用,那么不会产生AOP,这样spring就不能把你的代码植入到约定的流程中,于是就产生了失败...
  • insertOrder 尽管有@Transactional 注解,但它被内部方法 insert 调用,事务被忽略,出现异常事务不会发生回滚。 上面的两个问题@Transactional 注解只应用到 public 方法和自调用问题,是由于使用 Spring AOP 代理...
  • @Transactional的自调用失效问题

    千次阅读 2018-02-01 20:52:23
    注解@transactional的底层实现是Spring AOP技术,而Spring AOP技术使用的是动态代理。这就意味着对于静态(static... 所谓的自调用是指一个类的一个方法去调用自身另外一个方法的过程。看代码: @Transactional(pr
  • 有时候我们在接口方法中配置了@Transactional的注解,但实际使用时却遇到@Transactional注解失效的问题,我们在这里选取一个比较隐秘的细节问题来剖析失效问题。 问题原因分析 注解@Transactional的底层实现是Spring...
  • @Transactional的自调用失效问题   有时候配置了注解@Transactional,但是它会失效,这里要注意一些细节问题,以避免落入陷阱。 注解@Transaction的底层实现是Spring AOP技术,而Spring AOP技术使用的是动态代理...
  • 1.问题1的提出 class AServiceImpl{ @AutoWide BService bService; @Transactional public void a() { bService.b(); bService.c();... @Transactional(propagation = Propagation.REQUIRED,timeout
  • 该篇博客为总结自己曾写下的Bug 一、亲身案例 当时的场景为:在controller层获取一笔交易单的信息(前台传给controller层为Map类型的键值对),然后controller层直接将这个Map参数对象转发给Service层,在Service层...
  • @Transactional 进阶 1. @Transactional 注解的属性信息 属性 描述 name 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器 propagation 事务的传播行为,默认值为 ...
  • 错误原因: 调用方法saveA 和 saveB 是通过 实例对象调用的,而非Spring代理的Bean。 解决办法: 通过注入自身Bean调用本类中的方法,即可。
  • 下面写法可以不用写接口 @EnableAspectJAutoProxy( exposeProxy = true)要加,自定义事务管理器也要加,不然可能报错 No qualifying bean of type 'org.springframework.transaction.PlatformTransactionManager...
  • 转自:...方法A调用方法B: 1、如果只有A加@Transactional注解;则AB在同一事务中,任意异常都回滚; 2、如果只有B加@Transactional注解;AB方法为同一类,事务失
  • 与其说是坑,还不如说是自己事务这块儿太薄弱导致的(自嘲下)。 项目环境 Spring Boot 下面开始问题描述,发生的过程有点长,想直接看方案的直接跳过哦~! 最近在做项目中有个业务是每天定时更新xx的数据,某条记录...
  • SpringAOP 代理的Service对象调用了其方法,这个方法再去调用这个Service中的其他方法是没有使用AOP代理的对象去调用的所以也不会创建新的事物。 方案 1.再创建一个Service,不要在同一个类中调用。(那它们都是AOP...
  • 查询数据和处理数据,往往不需要事务,但是更新数据就涉及到了数据库的事务操作,所以就把更新操作单独的提取取来,写成另外一个方法,在此方法上加上Spring的@Transactional事务注解,被调用。然后问题就来了,在同...
  • 同一个类中方法调用,导致@Transactional失效 开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务...
  • Transactional在某些场景下会失效,这是值得注意的问题,上一节中,我们测试传播行为,是使用一个RoleBatchServiceImpl类去调用RoleServiceImpl类的方法,那么如果我们不创建RoleBatchServiceImpl类,而是只是使用...
  • 方法A调用方法B: 1、如果只有A加@Transactional注解;则AB在同一事务中; 2、如果只有B加@Transactional注解;AB方法为同一类,事务失效;AB不同类,只有B有事务; 原理: spring 在扫描bean的时候会扫描方法上是否...
  • @Transactional用于将多次使用sql对数据库进行操作的方法定义为一个原子操作(自己的理解,定义出来的。原子操作指将整个或者多个操作视作一个整体,若操作全部成功则成功执行,若有一个操作出现失败则全部操作进行...
  • 同一个类的不同方法,A方法没有@Transactional,B方法有@Transactional,A调用B方法,事务不起作用 原理解析: spring 在扫描bean的时候会扫描方法上是否包含@Transactional注解,如果包含,spring会为这个bean动态地...
  • @Transactional 同一个类中无事务方法a()内部调用有事务方法b()的问题 原创袁义锐 最后发布于2019-05-13 17:32:49 阅读数 2269 收藏 展开 1. 事务的4种特性 序号 参数 含义 1 原子性(Atomicity) 事务是数据库的逻辑...
  • 本文主要讨论Spring声明式事务中使用注解@Transactional的方式、原理及注意事项,主要包括以下内容: Spring @Transactional的配置使用; Spring @Transactional的传播行为和隔离级别; Spring @Tr...
  • @Transactional

    万次阅读 多人点赞 2018-01-09 21:28:33
    @Transactional 事务管理的目的 在出现异常的情况下,保证数据的一致性;数据提交操作回滚至异常发生前的状态 事务管理的方式: Spring(Spring Framework 提供对事务管理的抽象接口...
  • @Transactional用于开启事务,可以加在方法上或者类上,当加在方法上时,方法内部调用会由于不经过代理类而造成事物失效,如下: Class A { public vode m1(){ m2(); } @Transactional public vode m2(){ ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,752
精华内容 9,900
关键字:

transactional自身调用