V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
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 · 2014-06-10 22:21:37 +08:00 · 4814 次点击
    这是一个创建于 3806 天前的主题,其中的信息可能已经有所发展或是发生改变。
    是这样的:
    首先由两张表 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()方法中捕获异常 ,还可以接受 ,我在内部捕获异常且处理了 为什么还会回滚事物呢?

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


    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)
    第 2 条附言  ·  2014-06-17 21:16:41 +08:00
    问题初步得到解决了! 在这里把问题解决方法记录下:
    简单说就是将两个事务分开就可以了,
    进一步说,即将事务标记到insert 操作 的DAO层,在service控制异常循环调用DAO层的数据库操作实现重新id的筛选,最后返回id更新B表. 至此在我看来问题确实得到了解决.

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

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

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

    你说的 EM 是指EntityManager吗? 那我说的关闭连接应该是不准确的吧 就想异常信息中描述的那样 被标记回滚了是吗? 然后再次操作数据表就不被执行 这么理解是正确的吗?
    palmers
        9
    palmers  
    OP
       2014-06-17 21:17:36 +08:00
    @lszwycn 问题找到具体的解决方案了 ,麻烦你帮我看看 是否存在隐患?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2855 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 14:47 · PVG 22:47 · LAX 06:47 · JFK 09:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.