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.