› MySQL 5.5 Community Server
› MySQL 5.6 Community Server
› Percona Configuration Wizard
› XtraBackup 搭建主从复制
Great Sites on MySQL
› Percona
› MySQL Performance Blog
› Severalnines
推荐管理工具
› Sequel Pro
› phpMyAdmin
推荐书目
› MySQL Cookbook
MySQL 相关项目
› MariaDB
› Drizzle
参考文档
› http://mysql-python.sourceforge.net/MySQLdb.html
palmers
V2EX  ›  MySQL

一个 JPA 事物的问题,请大家一定要戳进来帮我看下,谢谢了!

  •  
  •   palmers · Jun 10, 2014 · 5895 views
    This topic created in 4508 days ago, the information mentioned may be changed or developed.
    是这样的:
    首先由两张表 A . B
    A表中有一个字段 主键id 在B表中是外键 且 这两个表被两个以上系统同时使用.

    然后 流程中需要将生成四个 6位字符,挑选一个 A表不存在的字符添加到A表中。然后使用该字符关联数据到B表中.

    大概方法结构是这样的:

    @Transactional(rollbackFor=Exception.class,value="transactionManager")
    xxx F ()throws Exception{
    String enbleId=getxxx();//这里是循环将四个字符插入到A表 如果成功没有异常则使用该ID
    updatexxx(enbleId); //在这里经过得到的有效id 更新到B表
    }

    getxxx()方法结果是这样的:

    getxxx(){
    //准备好四个6位的字符串数组 ids [];
    for(String srcid : ids){
    try{
    //这里使用srcid insert 到A表如果异常 进入catch 进行下一次循环
    //如果没有问题 break; 结束循环 返回这个 有效字符串到上面方法 F ()
    return enbleid;
    } catch(Exception e){
    continue;
    }
    }
    }


    上面这种结果 ,会出现这样几个问题:

    1. 当 在getxxx 方法中 只要有主键唯一约束异常存在则在F() 处被事物管控回滚 ,即使 我使用catch 处理 然后得到有效字符串 。也会回滚并且抛出异常。
    2.在方法F ()中调用的getxxx 方法中一次生成的四个id 字符串 为了有效使用 依次插入 设想 异常 必定为主键惟一约束所以进入catch 进入下一轮循环,然后得到有效id insert 到A表 然后返回再更新到B表。
    但是 并不是设想的那般, 当在getxxx 方法中存在依次主键约束异常,得到有效id后 回到F()方法都会被JPA事物拦截 回滚事物 然后导致B表不能更新抛出异常

    请问这是为什么 ?
    如果我在F()方法中捕获异常 ,还可以接受 ,我在内部捕获异常且处理了 为什么还会回滚事物呢?

    之前想先查询 再插入,害怕中间的时间差导致 主键约束 所以采用这种方法,但是现在确实很多问题 只要重复就会失败
    请问这种问题应该如何解决???
    Supplement 1  ·  Jun 11, 2014
    部分异常信息:


    java.lang.RuntimeException: org.hibernate.exception.ConstraintViolationException: error executing work
    at com.sinosoft.one.data.jpa.repository.query.OneJadeRepositoryQuery.execute(OneJadeRepositoryQuery.java:42)
    at com.sinosoft.one.data.jpa.repository.support.OneRepositoryFactorySupport$OneQueryExecutorMethodInterceptor.invoke(OneRepositoryFactorySupport.java:338)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:91)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at com.sun.proxy.$Proxy537.tCorrespondSave(Unknown Source)
    Supplement 2  ·  Jun 17, 2014
    问题初步得到解决了! 在这里把问题解决方法记录下:
    简单说就是将两个事务分开就可以了,
    进一步说,即将事务标记到insert 操作 的DAO层,在service控制异常循环调用DAO层的数据库操作实现重新id的筛选,最后返回id更新B表. 至此在我看来问题确实得到了解决.

    这样就能实现JPA忽略异常事务回滚数据重新开始另一个事务了
    这样也就不会造成主键约束联动的外键约束异常了!
    9 replies  •  2014-06-17 21:17:36 +08:00
    palmers
        1
    palmers  
    OP
       Jun 10, 2014
    哦对了 使用springmvc
    palmers
        2
    palmers  
    OP
       Jun 11, 2014
    @Livid 麻烦给看看 谢谢 !
    palmers
        3
    palmers  
    OP
       Jun 11, 2014
    @Livid 老大 能不能把我这个问题 移动到 技术节点?? 顺便 帮我看看这个问题 我实在是搞不定 非常感谢 !
    lszwycn
        4
    lszwycn  
       Jun 11, 2014
    因为JPA抛出的都是RuntimeException, 你只catch了Exception
    palmers
        5
    palmers  
    OP
       Jun 11, 2014
    @lszwycn Thank you ! 但是在getxxx 方法中我使用 Exception 确实捕获到了 然后也按照我的设想 进入了下一次循环 然后的到 id 但是在该方法调用处 却因为之前的 主键约束异常 被事务回滚了 ? 为什么??
    palmers
        6
    palmers  
    OP
       Jun 11, 2014
    @lszwycn 而且 Exception 是父类为什么会捕获不到呢?
    lszwycn
        7
    lszwycn  
       Jun 11, 2014
    按照JPA的规范, 如果一个数据库操作抛出了jpa的异常, 那么应该回滚, 关闭EM, 因为这时候, 已经不保证EM内部的状态了, 你这里, 如果我没理解错的话, 是在getxxx的方法里面抛出了异常了, 然后catch了之后, 有继续操作了EM
    palmers
        8
    palmers  
    OP
       Jun 11, 2014
    @lszwycn 是这样的 在getxxx 中有持久化操作且有异常catch 了之后 再次进行 持久化操作 ,

    “按照JPA的规范, 如果一个数据库操作抛出了jpa的异常, 那么应该回滚, 关闭EM” 你这句话的意思 是不是这样的 :

    我这里的getxxx 方法中 第一次主键约束异常发生,虽然我catch 了 也按照我的方式捕获到了异常, 进行下一次循环,但是这时候 已经关闭了A表连接,所以我虽然下次循环得到了有效的id 执行了 insert A表,但实际并没有持久化操作,导致后面更新B表的时候就发生外间不存在异常 然后事务回滚了
    是这样的吗?

    你说的 EM 是指EntityManager吗? 那我说的关闭连接应该是不准确的吧 就想异常信息中描述的那样 被标记回滚了是吗? 然后再次操作数据表就不被执行 这么理解是正确的吗?
    palmers
        9
    palmers  
    OP
       Jun 17, 2014
    @lszwycn 问题找到具体的解决方案了 ,麻烦你帮我看看 是否存在隐患?
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   3110 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 14:19 · PVG 22:19 · LAX 07:19 · JFK 10:19
    ♥ Do have faith in what you're doing.