hibernate sets dirty flag (and issues update) even though client did not change value

不羁岁月 提交于 2020-02-04 08:43:44

问题


hibernate dirty flag works great for what they call "persistent" objects https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/objectstate.html

but i have a web app, where the hib mang entity Order is also used as the Spring MVC command object. so when my controller marshals the request into an order, it is what hibernate calls a "detached" object. my problem senario can be illustrated with 2 web clients and 4 web requests against a single order.

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

in step 3, client 1 issues session.update(order) (also tried session.merge() and session.saveOrUpdate()) and ends up reverting attribute 1 back to its original value. if it was a "persistent" object, then hibernate would have known that nothing had changed. but because it is a detached object, hibernate actually issues an update for every member of the Order object.

Any ideas on how to get the dirty flag in hibernate detached objects to work like it does in persistent objects?

test first test shows hibernate working with persistent objects, works great, session 1 issues no updates:

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");

then here is the same idea but with detached objects and client 1 ends up reverting the update made by client 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");

回答1:


you're pretty much stuck using an interceptor for this. hibernate is VERY AGGRESSIVE about detecting deltas on an object, to the point of not caring if the value actually deltas, only that the mutator methods are called.

it is annoying but the overhead of recursive descent and the possibility of circularity preclude a more definitive dirty detection on an abstract level.




回答2:


You don't need to call save if the entity is detached. That is meant only for persisting a transient entity.

In your case you need to merge the detached entity and make sure you use optimistic locking too, because you use two different Sessions (one for loading and the second for updating) and meanwhile the actual database object might have changed by some other concurrent request.

If you use property-level access, then you need to make sure you don't mess with the actual Hibernate collections, like when wrapping them into some other collection object. In that case, Hibernate will detect those collections as dirty, even if the actual content hasn't changed.



来源:https://stackoverflow.com/questions/27595252/hibernate-sets-dirty-flag-and-issues-update-even-though-client-did-not-change

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!