【问题标题】:Hibernate Select on merge() seems useless合并()上的休眠选择似乎没用
【发布时间】:2016-03-17 11:05:03
【问题描述】:

我有一个表,我试图理解 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 年到 2000 年的 c02,以便在保留 1953 年并将字段更新为 1953 年和 1953 年替换数据库中的 2000 年时不会伤害任何人的工作当然是工作从另一个人那里丢失了。

我在互联网上阅读了一些内容,并且看到了类似的内容基本上,如果您没有版本或时间戳字段,Hibernate 必须在更新之前检查现有记录,以便不会发生并发修改。您不希望在您阅读后被其他人修改的记录更新。上面的链接中概述了几种解决方案。但是,如果可以在每个表上添加一个版本字段,它会让生活变得更加轻松。听起来不错,但是在更新它之前不会发生并发修改这不会发生 Hibernate 只是更新字段即使它们在当前数据库记录中不一样,我也在我的课堂上。

Hibernate 必须在更新之前检查现有记录检查 hibernate 检查什么?

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

我知道这个问题有点简单或重复,但我只是看不出触发选择的逻辑或好处。

简历

合并后 Hibernate 正在更新所有属性,即使是那些未修改的我也不知道为什么会这样1 次或手动修改我认为合并是无用的。

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

【问题讨论】:

    标签: java mysql hibernate select merge


    【解决方案1】:

    您的问题似乎包含两个问题。

    动态更新语句

    Hibernate 从 4.1 开始支持 @DynamicUpdate

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

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

    这仅仅意味着在打开的会话范围内,任何附加到带有@DynamicUpdate 标记的会话的实体都将跟踪字段级别的更改,并且只发出仅包含已更改字段的 DDL 语句。

    如果您的实体从会话中分离并且您发出合并,@SelectBeforeUpdate 注释会强制休眠从数据库中刷新实体,将其附加到会话,然后确定脏属性以便编写 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 上所有会导致状态更改的操作都需要使用锁进行查询,以防止并发问题。

    乐观

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

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

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

    悲观锁定是一种主动锁定机制,而乐观则更加反动

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-30
      • 2017-02-19
      • 2015-01-27
      • 1970-01-01
      • 1970-01-01
      • 2012-02-09
      • 2011-04-12
      • 1970-01-01
      相关资源
      最近更新 更多