代码之家  ›  专栏  ›  技术社区  ›  Paul Taylor

在Hibernate中,当对象已经存在于数据库中时,saveOrUpdate为什么会给出异常

  •  1
  • Paul Taylor  · 技术社区  · 6 年前

    saveOrUpdate() 据我所知,如果Hibernate发现它已经被添加,它只会更新,并由我的save进行更改。

    但是它失败了

    18/08/2018 21.58.34:BST:Errors:addError:SEVERE: Adding Error:Database Error:Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.jthink.songlayer.MusicBrainzReleaseWrapper#95f6f584-407f-4b26-9572-bb8c6e9c580a]
    java.lang.Exception
        at com.jthink.songkong.analyse.general.Errors.addError(Errors.java:28)
        at com.jthink.songkong.exception.ExceptionHandling.handleHibernateException(ExceptionHandling.java:209)
        at com.jthink.songkong.db.ReleaseCache.addToDatabase(ReleaseCache.java:394)
        at com.jthink.songkong.db.ReleaseCache.add(ReleaseCache.java:65)
    
    
    
    
    @Entity
        public class MusicBrainzReleaseWrapper
        {
            @Id
            private String guid;
    
            @Version
            private int version;
    
            @org.hibernate.annotations.Index(name = "IDX__MUSICBRAINZ_RELEASE_WRAPPER_NAME")
            @Column(length = 1000)
            private String name;
    
            @Lob
            @Column(length = 512000)
            private String xmldata;
    
            public String getGuid()
            {
                return guid;
            }
    
            public void setGuid(String guid)
            {
                this.guid = guid;
            }
    
            public String getName()
            {
                return name;
            }
    
            public void setName(String name)
            {
                this.name = name;
            }
    
            public String getXmldata()
            {
                return xmldata;
            }
    
            public void setXmldata(String xmldata)
            {
                this.xmldata = xmldata;
            }
        }
    
        private static boolean addToDatabase(Release release)
            {
                Session session = null;
                try
                {
                    session = HibernateUtil.beginTransaction();
                    //Marshall to String
                    StringWriter sw = new StringWriter();
                    Marshaller m = jc.createMarshaller();
                    m.marshal(release, sw);
                    sw.flush();
    
                    MusicBrainzReleaseWrapper wrapper = new MusicBrainzReleaseWrapper();
                    wrapper.setGuid(release.getId());
                    wrapper.setName(release.getTitle().toLowerCase(Locale.UK));
                    wrapper.setXmldata(sw.toString());
                    session.saveOrUpdate(wrapper);
                    session.getTransaction().commit();
                    MainWindow.logger.info("Added to db release:" + release.getId() + ":" + release.getTitle());
                    return true;
                }
                catch (ConstraintViolationException ce)
                {
                    MainWindow.logger.warning("Release already exists in db:"+release.getId()+":"+release.getTitle());
                    return true;
                }
                catch(GenericJDBCException jde)
                {
                    MainWindow.logger.log(Level.SEVERE, "Failed:" +jde.getMessage());
                    ExceptionHandling.handleDatabaseException(jde);
                }
                catch(HibernateException he)
                {
                    MainWindow.logger.log(Level.SEVERE, "Failed:" +he.getMessage());
                    ExceptionHandling.handleHibernateException(he);
                }
                catch(Exception e)
                {
                    MainWindow.logger.log(Level.WARNING,"Failed AddReleaseToDatabase:"+release.getId()+ ':' +e.getMessage(),e);
                    throw new RuntimeException(e);
                }
                finally
                {
                    HibernateUtil.closeSession(session);
                }
                return false;
            }
    

    用于在调用addToDatabase之前首先检查

            if(ReleaseCache.get(release.getId())==null)
            {
                addToDatabase(release)
            }        
    
    4 回复  |  直到 6 年前
        1
  •  1
  •   Alexander Petrov    6 年前

    您遇到的问题与乐观锁定直接相关 您已通过 MusicBrainzReleaseWrapper上的注释。saveOrUpdate确实可以添加或更新实体,但前提是实体版本与您试图添加或合并的分离对象的版本相同。

    在您的特定示例中,分离的对象的版本早于数据库中的最后一个版本,因此无法对过时的数据执行该操作。

    MusicBrainzReleaseWrapper wrapper = session.get(release.getId()):
    //the wrapper is managed object
    if (wrapper == null) {
      //initilize wrapper with the values from release
      .......
      session.save(wrapper)
    }
    else {
       // do not set ID here. ID is aready present!!!
       //  never manuay set the version field here
       wrapper.setName(release.getTitle().toLowerCase(Locale.UK));
       wrapper.setXmldata(sw.toString());
       session.saveOrUpdate(wrapper);
       //In case you don't need update logic at all
       // remove the @Version field from the entity
       // and do othing in the else clause , or throw exception
       // or log error or anything you see fit
    }
    
        2
  •  2
  •   Pavan    6 年前

    一个实体的hibernate对象有3个状态。他们是: -暂时的或新的 -持久(对象从数据库中获取,并且hibernate会话处于打开状态) 在save or update方法中,它要么保存临时对象,要么更新分离/持久对象。 在您的代码中,您正试图创建Transient/New对象并在其中设置旧的id。这就是你克服错误的原因。首先使用id获取对象,然后更新它的正确方法。

        3
  •  1
  •   menteith    6 年前

    saveOrUpdate 方法用于将实体持久化或与当前会话合并。它不像你期望的那样。保存或更新实体是应用程序的特定逻辑。Hibernate不执行任何应用程序的特定逻辑。

        4
  •  1
  •   df778899    6 年前

    Session.merge()

    @在Hibernate(或JPA)术语中,Pavan关于实体是暂时的或分离的说法是正确的。这两种状态都意味着Hibernate在其会话中(在 StatefulPersistenceContext

    • merge() 指示Hibernate停止并检查分离的实例。第一张支票是 @Id 会话中的值,但如果它不在那里,则必须命中数据库。
    • saveOrUpdate() 指示Hibernate调用方知道只检查 StatefulPersistenceContext 对于 @身份证

    保存或更新() 适用于以下情况(有或没有 @身份证 值)会话已经知道的。

    在您的情况下,显然Hibernate不知道分离的实例,因此您需要使用 合并() @身份证 价值。

    对于更新,Hibernate喜欢知道实体的先前状态。如果它使用动态更新(所以不更新所有列),这是有意义的,但否则您会认为它可以直接进行更新。我知道的唯一的选择是直接更新查询(通过HQL或JPQL),但是如果您有一个实体实例,这就不方便了。也许其他人知道怎么做。