merge()上的Hibernate Select似乎没用



我有一个表,我正在试图理解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();        
}

是的,我知道merge用于分离的对象和所有这些东西,但select背后真正的东西我只是想了一些事情。

  1. 如果当我从第一次检索记录时,字段c02=1953后者被更改为c02=2000,我只是想在进行合并时,他们会保留已经更改的新字段c02=2000,如果我在会话中不修改字段,他们会在更新中替换从1953到2000的原始c02,以免在保留1953并更新字段作为1953和1953替换数据库中的2000当然是从其他人的工作丢失了

我在互联网上读到了一些东西,我看到了这样的东西本质上,如果你没有版本或时间戳字段,Hibernate必须在更新之前检查现有记录,这样就不会发生并发修改。你不希望别人在你阅读后修改的记录被更新。上面的链接中列出了几个解决方案。但是,如果可以在每个表上添加一个版本字段,则会让生活变得更轻松听起来很棒,但在更新之前,请注意不要发生并发修改这并没有发生Hibernate只是更新我类中的字段,即使它们在当前DB记录中不相同。

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

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

我知道这个问题在某种程度上很简单或重复,但我看不出解雇一名精选员工的逻辑或好处。

恢复

合并后,Hibernate正在更新所有属性,即使是那些未修改的属性。我不知道为什么会这样。我只是认为Hibernate只会更新修改后的属性以获得性能,而当clazz加载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=?

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

动态更新语句

Hibernate从4.1开始就支持@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应用程序中,您可能希望通知用户重新查询页面,进行更改,然后重新保存表单以继续。

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

最新更新