我有一个表,我正在试图理解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背后真正的东西我只是想了一些事情。
- 如果当我从第一次检索记录时,字段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应用程序中,您可能希望通知用户重新查询页面,进行更改,然后重新保存表单以继续。
悲观锁定是一种主动锁定机制,乐观更反动。