我有一个表我试图理解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();
}
是的,我知道合并用于分离对象和所有这些东西,但是选择背后真正的东西我只是想到了一些东西。
我已经通过互联网阅读了一些东西,我看到类似这样的东西实质上,如果你没有版本或时间戳字段,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=?
答案 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应用程序中,您可能希望通知用户重新查询页面,进行更改并重新保存表单以继续。
悲观锁定是一种主动锁定机制,其中乐观更具反动性。