Spring-事务有时候不能回滚分析

开头总述

Spring在同一个类中调用function,事务会失效。

Spring事务是基于AOP代理来实现的。而AOP是使用JDK动态代理来实现的。

    例如:    

/**
	 * 父类调用子类
	 * 子类失败,不能影响父类
	 * 
	 * 预期效果:child回滚,parent插入成功
	 * 第一次试验 真实效果:都插入成功,child方法因为try catch导致事务未起作用。
	 */

第一次试验

  • code
        @Override
	@Transactional
	public void insertParent(){
		try {
			insertChild();
		} catch (Exception e) {
			// TODO: handle exception
		}
		
		user.setName("parent_5");
		user.setAge((byte)1);
		userMapper.insertSelective(user);
		
	}
	
	@Override
	@Transactional(propagation=Propagation.REQUIRES_NEW)
	public void insertChild(){
		user.setName("child_5");
		user.setAge((byte)1);
		userMapper.insertSelective(user);
		int i=1/0;
	}

  • 执行结果

  • 原因分析

    child方法被try catch住了,同时事务未生效。

    Spring事务不能回滚,根源是类内部调用是不走代理对象Proxy的。this.child走的是真实对象(真实对象)。导致child上的事务无效,先执行insert语句,执行成功以后才抛出异常。异常被parent中的try catch捕获到。不影响parent方法的执行。

第二次试验

  • code
@Override
	@Transactional
	public void insertParent(){		
		user.setName("parent_7");
		user.setAge((byte)1);
		userMapper.insertSelective(user);
		insertChild();		
	}
	
	@Override
	@Transactional(propagation=Propagation.REQUIRES_NEW)
	public void insertChild(){
		user.setName("child_7");
		user.setAge((byte)1);
		userMapper.insertSelective(user);
		int i=1/0;
	}

  • 执行结果

    

  • 原因分析

    去掉try catch,child方法抛出异常,导致回滚。同时异常向上抛,导致parent方法也回滚。

    child方法的事务失效,相当于child方法中的code挪到了parent方法中,也类似于在parent中抛出异常。


解决方案

  •     AOP需要先暴露出来。



  • 获取AOP代理需要从AOP的上下文来获取。得到当前AopProxy,然后通过AOP代理来调用child方法。
@Override
	@Transactional
	public void insertParent(){		
			
		try {
			/**
			 * AopContext.currentProxy()就类似于JDK代理中的
			 * Proxy.newInstance得到的Proxy对象
			 * UserService proxy=(UserService) AopContext.currentProxy();
			 * proxy.insertChild();
			 */
			((UserService)AopContext.currentProxy()).insertChild();
		} catch (Exception e) {
			// TODO: handle exception
		}		
		user.setName("parent_7");
		user.setAge((byte)1);
		userMapper.insertSelective(user);	
	}
	
	@Override
	@Transactional(propagation=Propagation.REQUIRES_NEW)
	public void insertChild(){
		user.setName("child_7");
		user.setAge((byte)1);
		userMapper.insertSelective(user);
		int i=1/0;
	}

  • 执行结果





你可能感兴趣的:(Spring)