[
http://opensource.atlassian.com/projects/hibernate/browse/HHH-3810?page=c...
]
Gail Badner commented on HHH-3810:
----------------------------------
Here is a more complicated test (also based on the test case from HHH-3046) that
illustrates the same bug caused by the fix in HHH-3229.
This test also failed before the fix was applied, but instead it was because a not-null
property references a transient value. This failure was originally documented in
HHH-3229.
-- (N : 1) -- Tour
|
| -- (1 : N) -- (pickup) ----
| | |
Route -- (1 : N) -- Node Transport
| |
-- (1 : N) -- (delivery) --
Collections are sets.
Node.route has cascade="none".
All other associations are cascade="merge,refresh".
All many-to-one associations have not-null="true"
There are 5 objects:
route (persistent)
deliveryNode (transient)
pickupNode (transient)
transport (transient)
tour (transient)
route.nodes { deliveryNode, pickupNode }
tour.nodes = { deliveryNode, pickupNode }
pickupNode.route = route
pickupNode.tour = tour
pickupNode.pickupTransports = { transport }
deliveryNode.route = route
deliveryNode.tour = tour
deliveryNode.deliveryTransports = { transport }
transport.pickupNode = pickupNode
transport.deliveryNode = deliveryNode
The following shows the execution path after applying the fix in HHH-3229, which results
in deliveryNode and transport being saved twice. The test ultimately fails with a
ConstraintViolationException because there are 2 Transport entities with the same
pickupNode.
MERGE EVENT route (persistent, routeID=1 )
MERGE EVENT deliveryNode
|
| A) CASCADE_BEFORE_SAVE deliveryNode
|
| MERGE EVENT tour
| |
| | A) CASCADE_BEFORE_SAVE tour (nothing to do)
| |
| | B) SAVE tour (tourID=2)
| |
| | C) CASCADE_AFTER_SAVE tour
| |
| | MERGE EVENT deliveryNode
| | | (BUG: embedded merge event for same transient entity!!!)
| | |
| | | A) CASCADE_BEFORE_SAVE deliveryNode
| | |
| | | MERGE EVENT tour (skip because it is already saved)
| | |
| | | B) SAVE deliveryNode (nodeID=3)
| | | (BUG: saved in embedded merge event!!!)
| | |
| | | C) CASCADE_AFTER_SAVE deliveryNode
| | |
| | | MERGE EVENT transport
| | | |
| | | | A) CASCADE_BEFORE_SAVE transport
| | | |
| | | | MERGE EVENT deliveryNode (skip because it is already saved)
| | | |
| | | | MERGE EVENT pickupNode
| | | | |
| | | | | A) CASCADE_BEFORE_SAVE pickupNode
| | | | |
| | | | | MERGE EVENT tour (skip because it is already saved)
| | | | |
| | | | | B) SAVE pickupNode (nodeID=4)
| | | | |
| | | | | C) CASCADE_AFTER_SAVE pickupNode
| | | | |
| | | | | MERGE EVENT transport
| | | | | | (BUG: embedded merge event for same transient entity!!!)
| | | | | |
| | | | | | A) CASCADE_BEFORE_SAVE transport
| | | | | |
| | | | | | MERGE EVENT deliveryNode (skip because it is already
saved)
| | | | | | MERGE EVENT pickupNode (skip because it is already saved)
| | | | | |
| | | | | | B) SAVE transport (transportID=5)
| | | | | | (BUG: saved in embedded merge event!!!)
| | | | | |
| | | | | | C) CASCADE_AFTER_SAVE transport (nothing to do)
| | | |
| | | | B) SAVE transport (transportID=6)
| | | | (BUG: saved again in original merge event!!!)
| | | |
| | | | C) CASCADE_AFTER_SAVE transport (nothing to do)
| | |
| | MERGE EVENT pickupNode (skip because it is already saved)
|
| B) SAVE deliveryNode (nodeID=7)
| (BUG: saved again in original merge event!!!)
|
| C) CASCADE_AFTER_SAVE deliveryNode
|
| MERGE EVENT transport (skip because it is already saved)
After merge completes, the action queue shows 6 inserts (should only be 4):
tour [ EntityInsertAction.id=2, tour.tourID=2 ]
pickupNode [ EntityInsertAction.id=3, pickupNode.nodeID=3 ]
deliveryNode [ EntityInsertAction.id=4, deliveryNode.nodeID=7 ] <-- inconsistent ID
transport [ EntityInsertAction.id=5, transport.transportID=6 ] <-- inconsistent ID
transport [ EntityInsertAction.id=6, transport.transportID=6 ]
deliveryNode [ EntityInsertAction.id=7, deliveryNode.nodeID=7 ]
When the session flushes, the test fails with a ConstraintViolationException because there
are 2 Transport entities with the same pickupNode
------------------------------------------------------------------------------------------------------------------
The following shows the execution path before applying the fix in HHH-3229, which fails
with org.hibernate.PropertyValueException because transport.pickupNode is transient when
transport is saved.
MERGE EVENT route (persistent)
MERGE EVENT deliveryNode
|
| A) CASCADE_BEFORE_SAVE deliveryNode
|
| MERGE EVENT tour
| |
| | A) CASCADE_BEFORE_SAVE tour (nothing to do)
| |
| | B) SAVE tour
| |
| | C) CASCADE_AFTER_SAVE tour
| |
| | MERGE EVENT deliveryNode (skip because it is already being merged)
| |
| | MERGE EVENT pickupNode
| | |
| | | A) CASCADE_BEFORE_SAVE pickupNode
| | |
| | | MERGE EVENT tour (skip because it is already being merged)
| | |
| | | B) SAVE pickupNode
| | |
| | | C) CASCADE_AFTER_SAVE pickupNode
| | |
| | | MERGE EVENT transport
| | |
| | | | A) CASCADE_BEFORE_SAVE transport
| | | |
| | | | MERGE EVENT deliveryNode (skip because it is already being merged)
| | | |
| | | | MERGE EVENT pickupNode (skip because it is already being merged)
| | | |
| | | | B) SAVE transport
| | | | (fails because transport.deliveryNode is non-nullable, but is still
transient!!!)
Transient entities can be inserted twice on merge
-------------------------------------------------
Key: HHH-3810
URL:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-3810
Project: Hibernate Core
Issue Type: Bug
Components: core
Affects Versions: 3.2.6, 3.3.0.GA, 3.3.1
Reporter: Gail Badner
Assignee: Gail Badner
Fix For: 3.2.x, 3.3.x, 3.5
A transient entity being merged can be inserted twice if there is a cascade back to that
same entity before it has been saved.
This bug was introduced by the fix for HHH-3229.
This can be illustrated by the following mapping (adapted from the test case at
HHH-3046):
Route -- (1 : N) -- Node -- (N : 1) -- Tour
There are 3 objects:
route (persistent)
pickupNode (transient)
tour (transient)
Collections are sets.
node.route has cascade="none".
All other associations are cascade="merge,refresh"
route.nodes { pickupNode }
tour.nodes = { pickupNode }
pickupNode.route = route
pickupNode.tour = tour
The following shows the execution path that results in pickupNode being saved twice.
MERGE EVENT route (persistent)
MERGE EVENT pickupNode (transient)
|
| A) CASCADE_BEFORE_SAVE pickupNode
|
| MERGE EVENT tour (transient)
| |
| | A) CASCADE_BEFORE_SAVE tour (nothing to do)
| |
| | B) SAVE tour
| |
| | C) CASCADE_AFTER_SAVE tour
| |
| | MERGE EVENT pickupNode (still transient)
| | | (BUG: embedded merge event for same transient entity!!!)
| | |
| | | A) CASCADE_BEFORE_SAVE pickupNode
| | |
| | | MERGE EVENT tour (skip because it is already merged)
| | |
| | | B) SAVE pickupNode
| | | (BUG: saved in embedded merge event!!!)
| | |
| | | C) CASCADE_AFTER_SAVE pickupNode
| | |
|
| B) SAVE pickupNode
| (BUG: saved again in original merge event!!!)
|
| C) CASCADE_AFTER_SAVE pickupNode (nothing to do)
Prior to applying the fix for HHH-3229, the execution path was:
MERGE EVENT route (persistent)
MERGE EVENT pickupNode (transient)
|
| A) CASCADE_BEFORE_SAVE pickupNode
|
| MERGE EVENT tour (transient)
| |
| | A) CASCADE_BEFORE_SAVE tour (nothing to do)
| |
| | B) SAVE tour
| |
| | C) CASCADE_AFTER_SAVE tour
| |
| | MERGE EVENT pickupNode (still transient; skip because it is already being
merged)
|
| B) SAVE pickupNode
|
| C) CASCADE_AFTER_SAVE pickupNode (nothing to do)
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
http://opensource.atlassian.com/projects/hibernate/secure/Administrators....
-
For more information on JIRA, see:
http://www.atlassian.com/software/jira