即使客户端没有更改值,hibernate也会设置脏标志(并发布更新)

时间:2014-12-22 00:24:42

标签: java hibernate jpa orm relational-database

hibernate dirty flag非常适合他们所称的" persistent"对象https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/objectstate.html

但我有一个Web应用程序,其中hib mang实体Order也用作Spring MVC命令对象。因此,当我的控制器将请求编组到一个命令中时,它就是hibernate所谓的"分离的"宾语。我的问题可以通过2个Web客户端和针对单个订单的4个Web请求来说明。

1 - client 1 opens the order in detail view
2 - client 2 opens the same order and updates attribute 1
3 - client 1 clicks "save" to update any changed fields, but actually has not updated anything

在步骤3中,客户端1发出session.update(order)(也尝试了session.merge()和session.saveOrUpdate())并最终将属性1恢复为其原始值。如果它是一个"持久的"对象,然后休眠就会知道没有任何改变。但由于它是一个分离的对象,hibernate实际上会为Order对象的每个成员发出更新。

关于如何在hibernate分离对象中获取脏标志的任何想法都像在持久对象中一样工作?

测试 第一个测试显示hibernate使用持久对象,效果很好,会话1没有发布更新:

try{Class.forName("net.sourceforge.jtds.jdbc.Driver");} catch(Exception e){e.printStackTrace();}

Session session1 = null;
Session session2 = null;


SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
session1 = sessionFactory.openSession();
session2 = sessionFactory.openSession();

Transaction tx1 = session1.beginTransaction();
Transaction tx2 = session2.beginTransaction();


// order 1 has a null dig user
//session 1 opens order 1
//session 2 opens order 1
//session 2 assigns andrew as dig, save
//session 1 hits save


Query qo1 = session1.createQuery("select o from  Order o where o.id = 536258");
Order o1 = (Order)qo1.list().get(0);

Query qo2 = session2.createQuery("select o from  Order o where o.id = 536258");
Order o2 = (Order)qo2.list().get(0);

 Query qu2 = session2.createQuery("select u from  User u where u.id = 7");
 User u2 = (User)qu2.list().get(0);

System.out.println("update user session 2 begin");
 o2.setDigitizer(u2);
System.out.println("update user session 2 end");


System.out.println("session 2 save begin");
session2.save(o2);
session2.flush();
tx2.commit();
session2.close();
System.out.println("session 2 save end");

System.out.println("session 1 save begin");
session1.save(o1);
session1.flush();
tx1.commit();
session1.close();
System.out.println("session 1 save end");

然后这是相同的想法,但使用分离的对象,客户端1最终还原客户端2所做的更新:

try{Class.forName("net.sourceforge.jtds.jdbc.Driver");} catch(Exception e){e.printStackTrace();}

Session session1 = null;
Session session2 = null;
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();

System.out.println("session 1a start");
session1 = sessionFactory.openSession();
Transaction tx1 = session1.beginTransaction();
Query qo1 = session1.createQuery("select o from  Order o where o.id = 536258");
Order o1 = (Order)qo1.list().get(0);
session1.flush();
tx1.commit();
session1.close();
System.out.println("session 1a end");


System.out.println("session 2 start");
session2 = sessionFactory.openSession();
Transaction tx2 = session2.beginTransaction();
Query qo2 = session2.createQuery("select o from  Order o where o.id = 536258");
Order o2 = (Order)qo2.list().get(0);
Query qu2 = session2.createQuery("select u from  User u where u.id = 7");
User u2 = (User)qu2.list().get(0);
o2.setDigitizer(u2); //here is session 2 update to the order
session2.flush();
tx2.commit();
session2.close();
System.out.println("session 2 end");

//here is the "detached" part, new session, but same domain object instance from above
System.out.println("session 1b start");
session1 = sessionFactory.openSession();
tx1 = session1.beginTransaction();
session1.update(o1); //tried merge -- same result, we revert the session 2 update
session1.flush();
tx1.commit();
session1.close();
System.out.println("session 1b end");

2 个答案:

答案 0 :(得分:1)

你很难使用拦截器来解决这个问题。 hibernate对于检测一个对象的增量是非常恼人的,如果值实际上是deltas,那就是不关心mutator方法被调用。

这很烦人,但是递归下降的开销和循环的可能性排除了抽象层次上更明确的脏检测。

答案 1 :(得分:1)

如果实体已分离,则您不需要调用save。这仅表示for persisting a transient entity

在您的情况下,您需要合并分离的实体并确保使用optimistic locking too,因为您使用两个不同的Sessions(一个用于加载,第二个用于更新),同时实际的数据库对象可能已更改为其他一些并发请求。

如果您使用属性级访问,那么您需要确保不要混淆实际的Hibernate集合,例如将它们包装到其他集合对象中时。在这种情况下,即使实际内容没有改变,Hibernate也会将这些集合检测为脏。