-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Author Message
 Post subject: Entity.postUpdate not called for versioned, readonly entity
PostPosted: Wed Feb 15, 2012 4:52 pm 
Newbie

Joined: Tue Feb 14, 2012 11:41 pm
Posts: 2
This is a summary of the scenario in question:

1. There's a parent entity (P) and a child entity (C).
2. The "mutable" attribute of class P is set to "false".
3. There's a "version" column specified on class P.
4. There's a bidirectional relationship, i.e. one-to-many from P to C and many-to-one from C to P.
5. The "inverse" attribute on the one-to-many side of the relationship on class P is set to "true".
6. The "cascading" attribute on the one-to-many side of the relationship on class P is “not” set.

This is what the code in question looks like:

Code:
Transaction txn = session.beginTransaction();

// create parent record
P p = new P();
session.save(p);
session.flush(); // line A

// add 1st child record
C c1 = new C();
c1.setParent(p);
session.save(c1);
session.flush(); // line B
p.getChildren().add(c1);

// add 2nd child record
C c2 = new C();
c2.setParent(p);
session.save(c2);
session.flush(); // line C
p.getChildren().add(c2);

// add 3rd child record
C c3 = new C();
c3.setParent(p);
session.save(c3);
session.flush(); // line D
p.getChildren().add(c3);

txn.commit();


The log information shows the following after "session.flush()" is called:

1. As the result of line A, an "insert into parent_table" SQL statement is executed to create the parent record;
2. As the result of line B, an "insert into child_table" SQL statement is executed to create the 1st child record;
3. As the result of line C, an "insert into child_table" SQL statement is executed to create the 2nd child record; and an "update parent_table set version_column" SQL statement is executed to increment the version column on the parent table;
4. As the result of line D, an "insert into child_table" SQL statement is executed to create the 3rd child record; and an "update parent_table set version_column" SQL statement is executed to increment the version column on the parent table.

The code above worked fine with 3.3.2 but failed with 3.6.6 at the step 4 while trying to update the version column on the parent table for the 3rd child -- a StaleObjectException was thrown.

By debugging into Hibernate's source code, I was able to determine that the reason the StaleObjectException was thrown was that the object "p" became out of the sync with (uncommitted) database values at the end of step 3 after the parent table has been updated with the new version number.

The reason the object "p" became out of sync was that the "entry.postUpdate" (line# 152 on EntityUpdateAction.class) did "not" get called.

The reason the "postUpdate" method was "not" called was primarily because the entity was marked as "read only" due to its"mutable" attribute being set to "false, hence the pre-conditions of executing "postUpdate" (at line# 131 on EntityUpdateAction class) not satisfied.

I have a couple of questions regarding the way Hibernate behaves in this scenario. Before I go into my questions, I'd like to make it abundantly clear that the Hibernate mappings as well as the code snippet cited above are "not" the point of the discussion. Although there're better ways to improve how the mappings and coding are written, what I really intend to go after is understand how the Hibernate is supped to behave with the given mappings and code snippet.

1. My first question is that since the parent end of the relationship (one-to-many) has "inverse" set to true, it means that it's the child class, not the parent class, that's responsible for managing the relationship. If so, when adding child objects into parent's collection as done in the code snippet below, I'd think it shouldn't be considered to be the change to the state of the parent entity (regardless of whether the parent entity is versioned or not), therefore, no update SQL statement should be issued against to the parent table simply because a child is added to the parent's collection. But in reality, the Hibernate does issue an update statement which updates nothing but the version column on the parent table.

2. If for whatever reasons Hibernate has to issue an update statement in the scenario above, why doesn't it also want to call the "entity.postUpdate" method to keep the version number on the entity object to be in sync with the version number in the database.

Compared to 3.3.2, the code in 3.6.6 has enhanced logic to handle "read only" entities. I was wondering whether the line 131 on EntityUpdateAction class should have also been enhanced to take into account the scenario where a "read only" entity is versioned and also possesses a dirty collection.

What I don't quite understand is why the logic that determines if an update action is necessary is "not" in sync with the logic that determines if "entity.postUpdate" should be called. If an update action is executed and "entity.postUpdate" is not called, wouldn't it cause the entity object to be out of sync with updated database values, hence StaleObjectException?

Any insight is greatly appreciated.


Top
 Profile  
 
 Post subject: Re: Entity.postUpdate not called for versioned, readonly entity
PostPosted: Thu Feb 16, 2012 4:06 am 
Expert
Expert

Joined: Tue Jun 16, 2009 3:36 am
Posts: 990
Hi jlinpa,

im trying to aswer you to question nr 1:

from a physical point of view you are right: as nothing but the version column changes also this could be avoided.

You can reach this by using the OptimisticLock(excluded=true) annotation on regarding relation:

Code:
 
@javax.persistence.OneToMany(mappedBy = "parentP")
@org.hibernate.annotations.OptimisticLock(excluded=true)
private Set<C> children = new java.util.HashSet<C>();


The reason why Hibernate by default does also increment the version on parent side,
is because beyoud the physical point of view, there exists also a (business) logical point of view which in most realities is more important.
Often the userrelies on the fact to have also a version increment also on the parent side.
Immagine following example: a bank uses Account as parent entity and associates Withdrawals with an inverse attribute.
Without version increment on the parent Account entity it would be possible that 2 withdrawals are be processed contemporaneously in distinct transaction on the same account without having any StaleObjectException, thus the account finally could be overdrawn.


Top
 Profile  
 
 Post subject: Re: Entity.postUpdate not called for versioned, readonly entity
PostPosted: Thu Feb 16, 2012 4:13 am 
Expert
Expert

Joined: Tue Jun 16, 2009 3:36 am
Posts: 990
P.S.: In my opinion, to use hiberante in the correct way an to be on the save side,
couples of set-instructions on bidirectional relations always belong together
without any other persistency related instructions (like save or flush) between.

Code:
c1.setParent(p);
p.getChildren().add(c1);


Top
 Profile  
 
 Post subject: Re: Entity.postUpdate not called for versioned, readonly entity
PostPosted: Fri Feb 17, 2012 10:07 am 
Newbie

Joined: Tue Feb 14, 2012 11:41 pm
Posts: 2
Hi pb00067,

First of all, thanks very much for sharing your thoughts; and second of all, I do agree with your comments regarding the code snippet itself -- I knew the code as well as the mappings could've been written differently to make them better. However, the intention of my post was to understand how and why Hibernate is designed to behave in a certain way with the given code snippet/mappings.

Going back to my question of why Hibernate issues an update statement for a versioned entity in spite of the fact that the changes have only occurred on a child collection which is explicitly marked as an "inversed" association, I understand your point. To be honest with you, before I posted my questions, I was trying to use the exactly same logic as you cited to convince myself that it's a valid design on the Hibernate part. However, I have to say that as much as I wanted to convince myself, I really felt that it was not a convincing argument. In fact, to put it a bit bluntly, I thought that it was a less thought-out design. My reasons are as follows:

The physical view point aside, from the logical view point, whether adding a child to the parent's collection constitutes a change of the parent's own state or not should really varies from case to case.

In your example where one account is associated with two withdrawals, it probably makes sense to lock the parent, i.e. the account, to guard against concurrent withdrawals. In fact, in such scenario, a withdrawal would typically cause the account's internal state to change, e.g. reduced balance.

But in a different application, it may be more desirable not to lock the parent to add children in order to achieve better concurrency level. For example, a forum website like this one can divide topics into a set of predefined sections, e.g. "Hibernate Users", "NHibernate Users. Registered users can select a section and post their questions. There's a parent-child relationships between a section and all the posts in the section. Let's say we don't care about at the section level how many posts there are and stats like that, and we want to allow users to post questions to the same section simultaneously, there’s no reason why we want to lock the selected section before each post can go through.

In my original example, Hibernate is explicitly told that the child collection on the parent side is an "inversed" relationship and the "cascade" attribute is absent. In other words, the intention clearly is to leave to the child's side to manage this relationship. Because of these settings, Hibernate will ignore any new child objects added to the parent's collection without issuing any insert statement, as the logical extension, there's really no good reason for Hibernate to still mark the collection as dirty and go ahead incrementing the parent's version number. If Hibernate is designed to observe the "inverse" setting and ignore child objects in the collection on the parent side, then those child objects should be ignored by the parent all the way through.

I’d have less issue with Hibernate had it only incremented the version number on the parent side for a non-inversed collection or the “cascade” attribute is present because Hibernate does actively manage the children from the parent side in these cases. However, for Hibernate to do what it does to an inversed collection, it really does not make too much sense.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.