`
xiangshouxiyang
  • 浏览: 49458 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

用spring目标对象处理Transaction rolled back because it has been marked as rollback-only

阅读更多

      在使用spring做事务管理时,很多人都会遇到这样一段异常:

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only 
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:718) 
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:475) 
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:270) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94) 
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96) 
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260) 
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94) 

 

 

出现上面问题的场景类似下面代码这样:

ITestAService:

 

package com.gigamore.platform.ac.service;
import com.onlyou.framework.exception.BusinessException;
public interface ITestAService {	
	void testA() throws BusinessException;
}

 

 

TestAService:

 

package com.gigamore.platform.ac.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.gigamore.platform.base.service.impl.BaseServiceImpl;
import com.onlyou.framework.exception.BusinessException;
@Service
public class TestAService extends BaseServiceImpl implements ITestAService{
	@Autowired
	private TestBService testBService;
	@Transactional
	public void testA(){
		try{
			testBService.testB();
		}catch(BusinessException e){
		    logger.info(e.getMessage());
		}catch(Exception e){
		    logger.info(e.getMessage());
		}
	}
}

 

 

TestBService:

 

package com.gigamore.platform.ac.service;

import java.util.Date;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.gigamore.platform.ac.entity.LoanProjectEntity;
import com.gigamore.platform.base.service.impl.BaseServiceImpl;
import com.onlyou.framework.exception.BusinessException;
@Service
public class TestBService extends BaseServiceImpl{
	@Transactional
	public void testB(){
		LoanProjectEntity project = this.selectByPrimaryKey(LoanProjectEntity.class, "2c9483e748321d4601485e1714d31412");
		project.setUpdDataTm(new Date());
		this.update(project);
		throw new BusinessException("抛异常");
	}
}

  测试用例:

 

 

@Autowired
	private ITestAService testAService;
	@Test
	public void testA() {
		testAService.testA();
	}

 

 

     testAService调用testBService的testB()方法,testB()方法里抛了一个BusinessException异常,但是testAService用try{}catch{}捕获异常并不往上层抛了。

     看起来好像没什么问题,异常被捕获了。其实不然,在testAService调用testBService的testB()方法时,会经过一次spring事务控制切面,事务切面里本身会对testBService的testB()方法进行异常捕获: TransactionAspectSupport.invokeWithinTransaction

 

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;
		}

     completeTransactionAfterThrowing(txInfo, ex)里面做了txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()),事务管理器做rollback, 把事务设置成rollback-only。 以上是testBService外层包装的事务切面做的事情。当testAService的testA()方法执行完,此时执行到testAService外层包装的事务切面,由于testA()方法执行过程没有抛出异常,所以事务正常提交,即执行的是commitTransactionAfterReturning(txInfo),事务对象txInfo对应的事务管理器进行提交事务,但事务已被设置为rollback-only,故spring对外抛出了Transaction rolled back because it has been marked as rollback-only异常。

    此时如果把testBService的testB方法写在testAService内部,即testA方法里用this.testB()调用,则不会出现以上的问题。因为testAService内方法testA调用方法testB,不会经过spring的事务切面。 由此引发出我用获得目标对象从而绕开spring事务切面的方法来避免该方法。

修改完之后的testAService的testA():

@Transactional
	public void testA(){
		try{
			//testBService.testB();
			AopTargetUtils.getTarget(testBService).testB();
		}catch(BusinessException e){
		    logger.info(e.getMessage());
		}catch(Exception e){
		    logger.info(e.getMessage());
		}
	}

  AopTargetUtils:根据spring代理对象获取目标对象,spring没有提供相应的工具类,以下代码是网上找的,我加上了泛型约束,用反射获得代理类内部的目标对象。

package com.gigamore.platform.ac.service;

import java.lang.reflect.Field;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;

public class AopTargetUtils {  
	  
    
    /** 
     * 获取 目标对象 
     * @param proxy 代理对象 
     * @return  
     * @throws Exception 
     */  
    public static <T> T getTarget(T proxy) throws Exception {  
          
        if(!AopUtils.isAopProxy(proxy)) {  
            return proxy;//不是代理对象  
        }  
          
        if(AopUtils.isJdkDynamicProxy(proxy)) {  
            return (T)getJdkDynamicProxyTargetObject(proxy);  
        } else { //cglib  
            return (T)getCglibProxyTargetObject(proxy);  
        }  
          
          
          
    }  
  
  
    private static Object getCglibProxyTargetObject(Object proxy) throws Exception {  
        Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");  
        h.setAccessible(true);  
        Object dynamicAdvisedInterceptor = h.get(proxy);  
          
        Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");  
        advised.setAccessible(true);  
          
        Object target = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();  
          
        return target;  
    }  
  
  
    private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {  
        Field h = proxy.getClass().getSuperclass().getDeclaredField("h");  
        h.setAccessible(true);  
        AopProxy aopProxy = (AopProxy) h.get(proxy);  
          
        Field advised = aopProxy.getClass().getDeclaredField("advised");  
        advised.setAccessible(true);  
          
        Object target = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget();  
          
        return target;  
    }  
      
}  

    这样,testAService调用testBService时,则类似Service内部的方法调用,不会经过spring事务切面,且此时testAService和testBService都在同一个事务里面,testBService的操作不会被回滚。

    不过,这样会引发一个问题,我们不仅绕过了spring的事务切面,也绕过了spring的其他的AOP切面,如果有用spring做日志记录之类的AOP处理,在执行testBService的testB()方法时,则不会做日志记录的处理。

    看了下评论里哥们说的用嵌套事务去处理这种情况,把TestBService
 的testB()方法的事务注解改成@Transactional(propagation = Propagation.NESTED),确实可以达到避免异常的效果。推荐用嵌套事务这种方法。本文中的方法有很大的局限性,仅仅可作为解决问题的一个思路。

 

 

分享到:
评论
3 楼 jhcfe 2016-11-08  
xiangshouxiyang 写道
jhcfe 写道
嵌套事务的时候建议看看这篇文章
http://sharajava.iteye.com/blog/78270
lz的需求我觉得用nested可以实现
methodAD(){ //事务属性nested
  try{
    methodBC();
  }catch ...//处理异常来决定是否外层回滚
}

这个看起来好像不是一码事,现在的场景是希望BC抛了异常,BC不回滚。AD也不回滚。整个事务都不希望回滚,所以才用了try{}catch{}

BC抛异常,BC也不回滚?那BC设为无事务的可否,不是很清楚这样添加事务管理的意义,AD异常了你们回不回滚?
2 楼 xiangshouxiyang 2016-11-07  
jhcfe 写道
嵌套事务的时候建议看看这篇文章
http://sharajava.iteye.com/blog/78270
lz的需求我觉得用nested可以实现
methodAD(){ //事务属性nested
  try{
    methodBC();
  }catch ...//处理异常来决定是否外层回滚
}

这个看起来好像不是一码事,现在的场景是希望BC抛了异常,BC不回滚。AD也不回滚。整个事务都不希望回滚,所以才用了try{}catch{}
1 楼 jhcfe 2016-11-07  
嵌套事务的时候建议看看这篇文章
http://sharajava.iteye.com/blog/78270
lz的需求我觉得用nested可以实现
methodAD(){ //事务属性nested
  try{
    methodBC();
  }catch ...//处理异常来决定是否外层回滚
}

相关推荐

    springboot2.0.3+activiti.5.22.0+oracle在线流程设计器(Activiti-Modeler )整合

    在IT领域,SpringBoot、Activiti和Oracle是三个非常重要的技术组件,它们分别代表了现代企业级应用开发的三大支柱:轻量级框架、工作流引擎和关系型数据库。本项目将这三者进行了深度整合,构建了一个高效、便捷的...

    COS——R.log

    org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only at org.springframework.transaction.support....

    org.springframework.transaction-3.2.2.RELEASE

    org.springframework.transaction-3.2.2.RELEASE最新版本

    transaction-springcloud-4.1.2.jar

    分布式事务框架LCN支持springcloud 2.0.5 ,transaction-springcloud-4.1.2.jar

    spring-transaction.jar.zip

    "spring-transaction.jar"正是提供了Spring事务管理的类库,它包含了一系列用于处理事务的接口、类和配置元素,使得开发者能够方便地实现事务控制。 一、Spring 事务管理概述 Spring事务管理分为编程式事务管理和...

    Spring-Struts-IBatis-AOP-Transaction

    编程式事务管理需要开发者手动调用 begin/commit/rollback 方法,而声明式事务管理则更简洁,只需在方法上添加 @Transactional 注解,Spring 就会自动处理事务的开始、提交和回滚。 **SSI 集成** 在 "17-Prj-...

    软件依赖包(Spring AOP+Hibernate Transaction)

    Spring AOP使用代理模式实现,可以是JDK动态代理或CGLIB代理,具体取决于目标对象是否实现了接口。通过切点(Pointcut)和通知(Advice),开发者可以精确地控制何时及如何执行这些关注点。 接下来,Hibernate ...

    javax.transaction-api-1.2-API文档-中文版.zip

    赠送jar包:javax.transaction-api-1.2.jar; 赠送原API文档:javax.transaction-api-1.2-javadoc.jar; 赠送源代码:javax.transaction-api-1.2-sources.jar; 赠送Maven依赖信息文件:javax.transaction-api-1.2....

    spring-hibernate-maven-transaction整合

    【Spring与Hibernate、Maven和Transaction的整合】 在现代Java Web开发中,Spring框架、Hibernate持久化工具和Maven构建工具是不可或缺的部分。Spring提供了强大的依赖注入和面向切面编程能力,Hibernate则简化了...

    Spring 常用 Transaction Annotation

    本篇主要聚焦于"Spring 常用 Transaction Annotation",即声明式事务管理,这是一种更简洁、易于维护的事务控制方式。 首先,Spring的声明式事务管理基于AOP(面向切面编程),它允许我们在不修改业务代码的情况下...

    org.springframework.transaction-3.1.0.M2

    org.springframework.transaction-3.1.0.M2

    springTransaction.rar

    这个名为"springTransaction.rar"的压缩包文件包含了一个关于Spring事务管理的小型示例,旨在演示如何使用Spring的事务传播机制来处理数据库操作,特别是转账功能的实现。 首先,让我们了解一下什么是事务。在...

    org.springframework.transaction-3.1.0.M1.jar包

    Spring中的事务管理问题:org.springframework.transaction-3.1.0.M1.jar包。org.springframework.transaction-3.1.0.M1.jar包。

    spring-transaction-demo.rar_DEMO_springboot_wash1vw

    通过`spring-transaction-demo`项目,我们可以学习到如何在Spring Boot应用中有效地使用事务管理,理解其背后的原理,以及如何处理事务边界内的异常情况。这个实战项目不仅有助于理论知识的理解,也提供了实际操作的...

    org.springframework.transaction-3.1.0.M1.jar

    org.springframework.transaction-3.1.0.M1.jar

    Could not roll back Hibernate transaction.doc

    Could not roll back Hibernate transaction; nested exception is org.hibernate.TransactionException: JDBC rollback failed 这表明Hibernate事务回滚操作失败,导致事务不能正确回滚。 二、问题原因 该问题的...

    微软内部资料-SQL性能优化3

    Intent locks improve performance because SQL Server examines intent locks only at the table level to determine whether a transaction can safely acquire a lock on that table. This removes the ...

    Spring2.0 事务处理

    在IT行业中,Spring框架是Java企业级应用开发的首选框架之一,尤其是在事务管理方面,它提供了强大而灵活的解决方案。Spring 2.0版本在事务处理方面做了许多改进,使得开发者能够更有效地管理应用程序的事务边界。这...

    org.springframework.transaction-3.2.4.RELEASE.jar

    org.springframework.transaction-3.2.4.RELEASE.jar,最新版的org.springframework.transaction,Wed Aug 07 16:44:37 GMT+01:00 2013

    「工控安全」asec-w04-derived-unique-token-per-transaction - APT.zip

    本文将围绕“asec-w04-derived-unique-token-per-transaction - APT”这一主题,深入探讨工控安全、数据泄露、勒索软件、大数据以及高级持续性威胁(APT)等关键知识点,同时也会提及安全集成、企业安全策略以及零...

Global site tag (gtag.js) - Google Analytics