merge()上的Hibernate Select似乎没用

时间:2015-12-11 13:54:07

标签: java mysql hibernate select merge

我有一个表我试图理解session.merge背后的逻辑,但我认为在某种程度上无用,我将尝试用一些代码解释更多。

public static void main(String[] args) 
{
    final Merge clazz = new Merge();
    Ragonvalia ragonvalia = clazz.load();//LOADED FROM DATABASE...
    System.out.println("ORIGINAL: "+ragonvalia);  
    //Prints c02=1953
    clazz.session.evict(ragonvalia);//WE EVICT HERE FOR FORCE MERGE RELOAD FROM DB
    //HERE I MAKE SOME MODIFICATIONS TO THE RECORD IN THE DB DIRECTLY.....
    try{Thread.sleep(20000);}catch(final Exception e){e.printStackTrace();}
    //now c02=2000
    final Ragonvalia merge = ragonvalia = (Ragonvalia)clazz.session.merge(ragonvalia);//MERGE IN FACT THE SELECT IS THROWN
    System.out.println("MERGING");
    System.out.println("merge: "+merge);
    System.out.println("ragonvalia: "+ragonvalia);
    System.out.println(merge.toString().equals(ragonvalia.toString()));//PRINT EQUALS
    ragonvalia.setC01("PUEBLO LINDO");//I MODIFIY THE C01 FIELD
    System.out.println(ragonvalia);
    final Transaction tx = clazz.session.beginTransaction();
    clazz.session.update(merge);//WE UPDATE  
    tx.commit();    
    //In this point i can see the c02 was reset again to 1953    
    clazz.shutDown();        
}

是的,我知道合并用于分离对象和所有这些东西,但是选择背后真正的东西我只是想到了一些东西。

  1. 如果我从第一次检索记录时,字段c02 = 1953后者被更改为c02 = 2000我只是在合并时,他们将保持新字段已更改c02 = 2000,如果我不修改我的会话中的字段,他们将从1953年更换c02,这是更新中的原始版本,以便在保留1953并更新字段时不会伤害任何人的工作,因为1953年和1953年取代了数据库中的2000当然是工作从另一个人那里迷失了。
  2. 我已经通过互联网阅读了一些东西,我看到类似这样的东西实质上,如果你没有版本或时间戳字段,Hibernate必须在更新之前检查现有记录,以便不会发生并发修改。在阅读之后,您不希望更新其他人修改过的记录。上面的链接中概述了几种解决方案。但是如果可以在每个表上添加一个版本字段,它会让生活变得更加容易。听起来不错但更新它之前不会发生并发修改这不会发生Hibernate只是更新字段即使在当前的DB记录中它们不相同,我也会在课堂上。

    Hibernate必须在更新之前检查现有记录检查hibernates检查的内容?

    事实上,我没有在我的模型中使用任何版本,但似乎合并仅用于检查数据库中是否存在记录。

    我知道这个问题有点简单或重复,但我无法看到触发选择的逻辑或好处。

    恢复

    合并之后Hibernate正在更新所有属性,即使那些未修改的属性我也不知道为什么这只是hibernate只更新修改后获得性能,并且当clazz加载时这些属性的值是相同的一次或手工修改我认为合并是没用的。

    update
        ragonvalia 
    set
        .....
        .....
        .....
        c01=?,
        c02=?,HERE IS 1953 EVEN WHEN THE MERGE WAS FIRED THE VALUE IN THE DB WAS 2000
        c03=? 
    where
        ID=?
    

1 个答案:

答案 0 :(得分:0)

您的问题混合了两个问题。

动态更新语句

自4.1以来,Hibernate一直支持@DynamicUpdate

  

对于更新,此实体是否应使用仅在更改时生成的动态sql   在准备好的sql语句中引用了列。

     

注意,对于重新附加分离的实体,如果没有,则无法实现   正在使用@SelectBeforeUpdate注释。

这仅表示在开放会话的范围内,附加到标记为@DynamicUpdate的会话的任何实体将跟踪字段级别更改,并仅发出仅包含已更改字段的DDL语句。

如果您的实体已从会话中解除并且您发出合并,@SelectBeforeUpdate注释会强制hibernate从数据库刷新实体,将其附加到会话,然后确定脏属性以便编写DDL仅包含更改字段的语句。

值得指出的是,这并不能防止您在高度并发的环境中对同一数据库记录的并发更新。这只是最大限度地减少大多数列未更改的旧表或宽表的DDL语句的方法。

并发更改

为了处理相同数据的并发操作,您可以使用两种类型的锁定机制来处理它。

<强>悲观

在这种情况下,您希望在读取时应用锁定,这基本上会强制数据库阻止任何其他事务读取/更改该行,直到锁定被释放。

由于这种类型的锁定会对性能产生严重影响,特别是在高度并发的表上,因此通常不首选。大多数现代数据库将达到行级锁定点,并最终将它们升级到数据页面或更糟糕的表格;导致所有其他会话阻塞,直到释放锁。

 public void alterEntityWithLockExample() {
   // Read row, apply a row level update/write lock.
   Entity entity = entityManager.find(Entity.class, 1L, LockModeType.PESSIMISTIC_WRITE);
   // update entity and save it.
   entity.setField(someValue);
   entityManager.merge(entity);
 }

可能值得注意的是,如果在上面的代码中应用写入锁之前有任何其他会话查询id为1的实体,则两个会话仍会相互踩踏。 Entity上导致状态更改的所有操作都需要使用锁进行查询,以防止出现并发问题。

<强> Opimistic

在高度并发的环境中,这是一种更受欢迎的方法。这是您要在实体上使用@Version注释新字段的位置。

这样做的好处是,您可以查询实体,将其分离,稍后重新附加,可能在另一个请求或线程中,更改它,并合并更改。如果记录在流程开始时最初被提取后发生了更改,则会抛出OptimisticLockException,允许您的业务案例处理您需要的情况。

在Web应用程序中,您可能希望通知用户重新查询页面,进行更改并重新保存表单以继续。

悲观锁定是一种主动锁定机制,其中乐观更具反动性