星空网 > 软件开发 > Java

项目中遇到的问题

一、Spring 事务问题

1.描述:service1 中的 a 调用 b,b 调用了 service2 中的 c ,c 调用了 service3 中的 d

期望:d 抛出异常时(我真实项目中抛出的是 Sql 异常),d,c 回滚,而 a,b 不回滚。

测试:考虑到 Spring 事务的自调用和 cglib 动态代理下的 spring 事务配置。添加了 <aop:aspectj-autoproxy expose-proxy="true" proxy-target-/>。

Demo:

自定义异常:

项目中遇到的问题项目中遇到的问题
public class MyException extends SQLException {  private static final long serialVersionUID = 1L;  public MyException() {    super();  }  public MyException(String reason, String sqlState, int vendorCode, Throwable cause) {    super(reason, sqlState, vendorCode, cause);  }  public MyException(String reason, String SQLState, int vendorCode) {    super(reason, SQLState, vendorCode);  }  public MyException(String reason, String sqlState, Throwable cause) {    super(reason, sqlState, cause);  }  public MyException(String reason, String SQLState) {    super(reason, SQLState);  }  public MyException(String reason, Throwable cause) {    super(reason, cause);  }  public MyException(String reason) {    super(reason);  }  public MyException(Throwable cause) {    super(cause);  }  }

MyException.java

Dao:

项目中遇到的问题项目中遇到的问题
@Repositorypublic class TxDao {  @Autowired  private JdbcTemplate jdbcTemplate;  public void updateA() {    String sql = "update tx_test set a_field = 1 where id = 1";    jdbcTemplate.update(sql);  }    public void updateB() {    String sql = "update tx_test set b_field = 1 where id = 1";    jdbcTemplate.update(sql);  }    public void updateC() {    String sql = "update tx_test set c_field = 1 where id = 1";    jdbcTemplate.update(sql);  }    public void updateD() {    String sql = "update tx_test set d_field = 1 where id = 1";    jdbcTemplate.update(sql);  }}

TxDao.java

ABService:

项目中遇到的问题项目中遇到的问题
@Servicepublic class ABService {  @Autowired  private TxDao txDao;  @Autowired  private CService cService;    @Transactional  public void aMethod() throws MyException {    System.out.println("aMethod");    txDao.updateA();    ((ABService) AopContext.currentProxy()).bMetod();  }    @Transactional  public void bMetod() throws MyException {    System.out.println("bMethod");    txDao.updateB();    cService.cMethod();  }    }

ABService.java

CService:

项目中遇到的问题项目中遇到的问题
@Servicepublic class CService {  @Autowired  private TxDao txDao;  @Autowired  private DService dService;  @Transactional(rollbackFor=MyException.class)  public void cMethod() throws MyException {    System.out.println("cMethod...");    txDao.updateC();    dService.dMethod();  }}

CService.java

DService:

项目中遇到的问题项目中遇到的问题
@Servicepublic class DService {  @Autowired  private TxDao txDao;    @Transactional(rollbackFor=MyException.class)  public void dMethod() throws MyException{    System.out.println("dMethod...");    txDao.updateD();    throw new MyException();  }}

DService.java

测试代码:

项目中遇到的问题项目中遇到的问题
@Testpublic void test() {  ABService service = context.getBean(ABService.class);  try {    service.aMethod();  } catch(MyException e) {    e.printStackTrace();  }}

View Code

(1)测试 rollbackFor 和 noRollbackFor

过程:

自定义了一个 Sql 异常 MyException 继承自 SQLException,从 d 抛出,一直向上抛。

对 a, b 的 @Transactional 的 noRollbackFor 属性设置为 MyException.class,而 c,d 的 rollbackFor 属性设置为 MyException.class。

控制台输出:

aMethod
bMethod
cMethod...
dMethod...

数据库输出:

项目中遇到的问题

测试结果: a,b,c,d 四个方法全部回滚。

原因查找:发现在容器初始化的时候就读取了所有的 事务方法的 RollBackFor 和  noRollBackFor 属性定义的 Value 值。

详细参见:org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(java.lang.Class<?>)

在某个事务方法抛出异常后,整个事务都进行了回滚,感觉与配置 noRollBackFor 没有关系。

详细参见:org.springframework.transaction.interceptor.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);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;}

在标红的地方会去掉目标方法,如目标方法抛出异常,则会进入到 catch 块,执行完 catch 块的代码继续向上抛出。catch 块能捕获到 MyException。

通过断点跟踪发现在 completeTransactionAfterThrowing() 方法里进行了回滚,等回滚之后最后去调用了 commitTransactionAfterReturning() 方法。

这样看来 noRollBackFor 甚至没有什么作用,有哪位大神看到这里并且知道原理的话,请不吝赐教,谢谢。

(2)测试 Spring 事务的传播行为。

过程:将 c 的传播行为改为 REQUIRES_NEW,其他还是默认。同时在 c 方法中处理了 d 抛上来的异常。

控制台输出:

aMethod
bMethod
cMethod...

数据库输出:

项目中遇到的问题

发现根本就不会去执行 d 方法。这里就不是很明白了。

上面提供了两个测试,事实上测试了  n 种方式,都没有行的通。等以后对 spring 事务理解加深之后再来解析。这里直接说最终的解决办法。

解决办法:

1.手动控制事务

2.服务拆分,比如这里 a,b 单独事务,c,d 单独事务。

 




原标题:项目中遇到的问题

关键词:

*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。

一周盘点:日本将考虑再次宣布紧急状态;Google搜索结果将能显示Instagram和TikTok视频:https://www.ikjzd.com/articles/139620
亚马逊在中国败诉:不得使用“AWS”标识、赔款7646万元:https://www.ikjzd.com/articles/139621
回望不凡2020 开启更好2021:https://www.ikjzd.com/articles/139622
你应该庆幸FaceBook错误提示:https://www.ikjzd.com/articles/139623
16%德国VAT税率政策还有吗?:https://www.ikjzd.com/articles/139624
封锁三年,沙特卡塔尔边境终于重新开放了!:https://www.ikjzd.com/articles/139625
跨境支付百科——巴西支付篇:https://www.kjdsnews.com/a/1836648.html
大福地快捷酒店预订 大福酒店怎么走:https://www.vstour.cn/a/365187.html
相关文章
我的浏览记录
最新相关资讯
海外公司注册 | 跨境电商服务平台 | 深圳旅行社 | 东南亚物流