Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » EclipseLink » 1.1.3 vs 2.0 equals
1.1.3 vs 2.0 equals [message #501455] Wed, 02 December 2009 12:05 Go to next message
Tom Eugelink is currently offline Tom EugelinkFriend
Messages: 817
Registered: July 2009
Senior Member
I have a @PrePersist on the child entities of a master-detail relation. In the @PrePersist is some checking done against the other childeren in the same master-detail relation.

At runetime, the child object that is executing the @PrePersist is actually a clone of an the object that is in the master-detail collection. So I need to use equals to determine if this is the same object.

if (!this.equals(lOther)) ...

The equals method includes the @Id and and @Version fields in the compare.

When running against 1.1.3 the cloned object does NOT yet have its @Id and @Version set, all four values are null.
When running against 2.0.0RC1 the cloned object DOES have its @Id and @Version set.
So equals behavior is not identical between 1.1.3 and 2.0.0RC1.

Is this change intentional?

Tom
Re: 1.1.3 vs 2.0 equals [message #501456 is a reply to message #501455] Wed, 02 December 2009 12:12 Go to previous messageGo to next message
Tom Eugelink is currently offline Tom EugelinkFriend
Messages: 817
Registered: July 2009
Senior Member
It also seems that in 1.1.3 the clone has the referring entities set (the child knows the master) and in 2.0.0 only has simple properties (string, but not other entities).

Tom
Re: 1.1.3 vs 2.0 equals [message #501737 is a reply to message #501455] Thu, 03 December 2009 16:09 Go to previous messageGo to next message
James Sutherland is currently offline James SutherlandFriend
Messages: 1939
Registered: July 2009
Location: Ottawa, Canada
Senior Member

Not sure I understand, what do your mean by "clone"? You should only have a single version of each object in the persistence unit.

The PrePersist will be called on the identical object that your application called persist() on. The object should be a new object, and should only have references to other new objects, or other managed objects.

Is your PrePersist being called from a merge()? If so, then the object being persisted will be a copy of the object merge() was called with.


James : Wiki : Book : Blog : Twitter
Re: 1.1.3 vs 2.0 equals [message #501741 is a reply to message #501737] Thu, 03 December 2009 16:17 Go to previous messageGo to next message
Tom Eugelink is currently offline Tom EugelinkFriend
Messages: 817
Registered: July 2009
Senior Member
I have an object in a master-child. When the object is actually persisted the @PrePerist is executed not on that object, but on a cloned version. The object is persisted by-relation (the master is persisted).

I'll see if I can get a stack trace of the PrePersist.

Tom


James wrote:
> Not sure I understand, what do your mean by "clone"? You should only
> have a single version of each object in the persistence unit.
>
> The PrePersist will be called on the identical object that your
> application called persist() on. The object should be a new object, and
> should only have references to other new objects, or other managed objects.
>
> Is your PrePersist being called from a merge()? If so, then the object
> being persisted will be a copy of the object merge() was called with.
>
Re: 1.1.3 vs 2.0 equals [message #502223 is a reply to message #501741] Mon, 07 December 2009 09:44 Go to previous messageGo to next message
Tom Eugelink is currently offline Tom EugelinkFriend
Messages: 817
Registered: July 2009
Senior Member
This is the simplified code in the Address child class. I inserted a println to show the hashcode of the compared entities.

@PrePersist @PreMerge
public void preSaveHook()
{
for (Address lAddress : getRelation().getAddressesWhereIAmRelation())
{
System.out.println( this.hashCode() + " vs " + lAddress.hashCode() );
if (!this.equals(lAddress))
{
...
}
}
}


There is one relation (master) with one new address (child), the relation is merged and the address is cascade persisted. This is the output:
17440602 vs 30741250

The compare happens between two different objects, both of class Address. If this is examined in debug mode all properties are identical, except:
- the "other" has a value for the @Id and @Version field, while for "this" these are null
- the "other" has no values for the relation property.

As mention in a previous post, in 1.1.3 this was different; neither the clone nor the original had @Id or @Version values and both had all fields set.

This is the stackstace, what seems important here is the method "registerNewObjectClone":
Thread [AWT-EventQueue-0] (Suspended)
Address.preSaveHook() line: 47
Address(AbstractBean<T>).prePersist() line: 250
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object...) line: 597
PrivilegedAccessHelper.invokeMethod(Method, Object, Object[]) line: 346
EntityClassListener(EntityListener).invokeMethod(Method, Object, Object[], DescriptorEvent) line: 297
EntityClassListener.invokeMethod(String, DescriptorEvent) line: 64
EntityClassListener(EntityListener).prePersist(DescriptorEve nt) line: 399
DescriptorEventManager.notifyListener(DescriptorEventListene r, DescriptorEvent) line: 670
DescriptorEventManager.notifyEJB30Listeners(DescriptorEvent) line: 606
DescriptorEventManager.executeEvent(DescriptorEvent) line: 200
RepeatableWriteUnitOfWork(UnitOfWorkImpl).registerNewObjectC lone(Object, Object, ClassDescriptor) line: 4237
RepeatableWriteUnitOfWork.cloneAndRegisterNewObject(Object) line: 507
RepeatableWriteUnitOfWork(UnitOfWorkImpl).internalRegisterOb ject(Object, ClassDescriptor) line: 2902
RepeatableWriteUnitOfWork(UnitOfWorkImpl).registerObject(Obj ect, ClassDescriptor) line: 4326
RepeatableWriteUnitOfWork(UnitOfWorkImpl).registerObject(Obj ect) line: 4284
OneToManyMapping(CollectionMapping).buildElementClone(Object , Object, UnitOfWorkImpl, boolean) line: 242
IndirectListContainerPolicy(ContainerPolicy).addNextValueFro mIteratorInto(Object, Object, Object, CollectionMapping, UnitOfWorkImpl, boolean) line: 264
OneToManyMapping(CollectionMapping).buildCloneForPartObject( Object, Object, Object, UnitOfWorkImpl, boolean) line: 195
TransparentIndirectionPolicy.cloneAttribute(Object, Object, Object, UnitOfWorkImpl, boolean) line: 148
OneToManyMapping(ForeignReferenceMapping).buildClone(Object, Object, UnitOfWorkImpl) line: 172
ObjectBuilder.populateAttributesForClone(Object, Object, UnitOfWorkImpl) line: 2690
RepeatableWriteUnitOfWork.cloneAndRegisterNewObject(Object) line: 502
RepeatableWriteUnitOfWork(UnitOfWorkImpl).internalRegisterOb ject(Object, ClassDescriptor) line: 2902
MergeManager.registerObjectForMergeCloneIntoWorkingCopy(Obje ct) line: 841
MergeManager.mergeChangesOfCloneIntoWorkingCopy(Object) line: 473
MergeManager.mergeChanges(Object, ObjectChangeSet) line: 267
RepeatableWriteUnitOfWork(UnitOfWorkImpl).mergeCloneWithRefe rences(Object, MergeManager) line: 3486
RepeatableWriteUnitOfWork.mergeCloneWithReferences(Object, MergeManager) line: 301
RepeatableWriteUnitOfWork(UnitOfWorkImpl).mergeCloneWithRefe rences(Object, int, boolean) line: 3446
EntityManagerImpl.mergeInternal(Object) line: 414
EntityManagerImpl.merge(T) line: 391
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object...) line: 597
EclipselinkEntityManagerExtender(EntityManagerExtender).invo ke(Object, Method, Object[]) line: 124
$Proxy3.merge(Object) line: not available
JpaObjectNavigatorModel$1.call() line: 812
JpaObjectNavigatorModel<T>.doSave() line: 825
JpaObjectNavigatorBar$4.actionPerformed(ActionEvent) line: 157
JButton(AbstractButton).fireActionPerformed(ActionEvent) line: 1995
AbstractButton$Handler.actionPerformed(ActionEvent) line: 2318
DefaultButtonModel.fireActionPerformed(ActionEvent) line: 387
DefaultButtonModel.setPressed(boolean) line: 242
BasicButtonListener.mouseReleased(MouseEvent) line: 236
AWTEventMulticaster.mouseReleased(MouseEvent) line: 272
JButton(Component).processMouseEvent(MouseEvent) line: 6263
JButton(JComponent).processMouseEvent(MouseEvent) line: 3267
JButton(Component).processEvent(AWTEvent) line: 6028
JButton(Container).processEvent(AWTEvent) line: 2041
JButton(Component).dispatchEventImpl(AWTEvent) line: 4630
JButton(Container).dispatchEventImpl(AWTEvent) line: 2099
JButton(Component).dispatchEvent(AWTEvent) line: 4460
LightweightDispatcher.retargetMouseEvent(Component, int, MouseEvent) line: 4574
LightweightDispatcher.processMouseEvent(MouseEvent) line: 4238
LightweightDispatcher.dispatchEvent(AWTEvent) line: 4168
JFrame(Container).dispatchEventImpl(AWTEvent) line: 2085
JFrame(Window).dispatchEventImpl(AWTEvent) line: 2478
JFrame(Component).dispatchEvent(AWTEvent) line: 4460
EventQueue.dispatchEvent(AWTEvent) line: 599
EventDispatchThread.pumpOneEventForFilters(int) line: 269
EventDispatchThread.pumpEventsForFilter(int, Conditional, EventFilter) line: 184
EventDispatchThread.pumpEventsForHierarchy(int, Conditional, Component) line: 174
EventDispatchThread.pumpEvents(int, Conditional) line: 169
EventDispatchThread.pumpEvents(Conditional) line: 161
EventDispatchThread.run() line: 122
Re: 1.1.3 vs 2.0 equals [message #502302 is a reply to message #502223] Mon, 07 December 2009 16:11 Go to previous messageGo to next message
James Sutherland is currently offline James SutherlandFriend
Messages: 1939
Registered: July 2009
Location: Ottawa, Canada
Senior Member

From your stack you are doing a merge() of the master, not a persist(). Merge must always create a clone of the objects, and the merged object is a clone.

Because the event is fired during the merge process, the state of its related objects may undefined, as it is in the processes of merging them.


James : Wiki : Book : Blog : Twitter
Re: 1.1.3 vs 2.0 equals [message #502357 is a reply to message #502302] Mon, 07 December 2009 19:05 Go to previous messageGo to next message
Tom Eugelink is currently offline Tom EugelinkFriend
Messages: 817
Registered: July 2009
Senior Member
James wrote:
> From your stack you are doing a merge() of the master, not a
> persist(). Merge must always create a clone of the objects, and the
> merged object is a clone.
>
> Because the event is fired during the merge process, the state of its
> related objects may undefined, as it is in the processes of merging them.

I do not quite understand this. I have a new entity; it is persisted by relation, why is the action being done on the parent of influence on the action done on my new entity; it is new, it is persisted.

Secondly, in 1.1.3 the behavior was different:
- the @Id and @Version were not set on the clone
- the related entities were set

Why this changed so much? I had to strip down the equals method tremendously (take out both @Id, @Version and any non primary properties) which makes it far less accurate. If the state is undefined, can it please be undefined as it was in 1.1.3? :-)

Tom
Re: 1.1.3 vs 2.0 equals [message #502719 is a reply to message #501455] Wed, 09 December 2009 14:35 Go to previous messageGo to next message
James Sutherland is currently offline James SutherlandFriend
Messages: 1939
Registered: July 2009
Location: Ottawa, Canada
Senior Member

persist() and merge() are two different operations. persist takes a new object and make that instance managed in the persistence context.

merge() is for when you have a detached, or serialized copy of an object. You want to merge the changes from your copy, but not effect your detached copy in any way. So the new object being merged must remain detached, and a copy of it persisted.

I'm not exactly sure what changed between 1.1.3 and 2.0, but if 1.1.3 was calling the prePersist event before the copy had its Id or version set, then it would seem to be more correct in 2.0, as it would seem desirable for the object to have its Id.

EclipseLink does have other events, depending on what you are doing you may wish to look into those, such as the preInsert DescriptorEvent.


James : Wiki : Book : Blog : Twitter
Re: 1.1.3 vs 2.0 equals [message #502730 is a reply to message #502719] Wed, 09 December 2009 15:04 Go to previous messageGo to next message
Tom Eugelink is currently offline Tom EugelinkFriend
Messages: 817
Registered: July 2009
Senior Member
> persist() and merge() are two different operations. persist takes a new
> object and make that instance managed in the persistence context.
>
> merge() is for when you have a detached, or serialized copy of an
> object. You want to merge the changes from your copy, but not effect
> your detached copy in any way. So the new object being merged must
> remain detached, and a copy of it persisted.

Ok. But given the situation where there is a master entity that already existed, so it needs to be merged, but it has a new child entity, which should be persisted...

Am I correct that a good approach could be to never use cascade persist or merge, and explicitly call persist on all objects? In that way the master is merged and the new child is persisted?


> EclipseLink does have other events, depending on what you are doing you
> may wish to look into those, such as the preInsert DescriptorEvent.

All I want is to compare and validate the properties of related entities before persist or merge. I'll take a look.

Tom
Re: 1.1.3 vs 2.0 equals [message #502934 is a reply to message #502719] Thu, 10 December 2009 11:02 Go to previous messageGo to next message
Tom Eugelink is currently offline Tom EugelinkFriend
Messages: 817
Registered: July 2009
Senior Member
> I'm not exactly sure what changed between 1.1.3 and 2.0, but if 1.1.3
> was calling the prePersist event before the copy had its Id or version
> set, then it would seem to be more correct in 2.0, as it would seem
> desirable for the object to have its Id.

1. I can live with the primary key change, but the fact that related entities are not set (the child doesn't have its master set) is causing me major headaches. Why are these values not cloned?

2. And I'm still not quite over the merge vs persist problem. As it turns out it is possible to merge a unpersisted entity (in this case the child via cascade merge), and that child then correctly is assigned a PK and inserted. That is the behavior of a persist.
Why isn't the behavior of EclipseLink something in the line of:
If an entity is merged but is in fact insert, then:
- it is treated as a persist and not a merge
or:
- an exception is thrown, telling that this entity cannot be a merged since it has never been persisted.
Re: 1.1.3 vs 2.0 equals [message #502956 is a reply to message #502934] Thu, 10 December 2009 12:35 Go to previous messageGo to next message
Tom Eugelink is currently offline Tom EugelinkFriend
Messages: 817
Registered: July 2009
Senior Member
> I can live with the primary key change, but the fact that related
> entities are not set (the child doesn't have its master set) is causing
> me major headaches. Why are these values not cloned?

Another problematic situation: the master already existed so it is merged, but the child is new and is persisted. In the child's PrePersist the code "getMaster().getAllChilderen()" is called; this collection is empty although we know for a fact that there must be at least one child (since we are in its @PrePersist event).

I'm now going to test these Eclipselink events and hope that will give me a more consistent data set. Otherwise I have to move back to 1.1.3 / 1.2

Tom
Re: 1.1.3 vs 2.0 equals [message #502993 is a reply to message #502956] Thu, 10 December 2009 14:32 Go to previous messageGo to next message
Tom Eugelink is currently offline Tom EugelinkFriend
Messages: 817
Registered: July 2009
Senior Member
> I'm now going to test these Eclipselink events and hope that will give
> me a more consistent data set. Otherwise I have to move back to 1.1.3 / 1.2

Pffft, initial test seem to indicate that redirecting calls from a DescriptorEventListener onto the now de-annotated JPA event methods, does seem to result in a much more predictable dataset.

More testing required.

Tom
new events [message #503037 is a reply to message #502993] Thu, 10 December 2009 16:15 Go to previous messageGo to next message
Tom Eugelink is currently offline Tom EugelinkFriend
Messages: 817
Registered: July 2009
Senior Member
This is the code that I execute:

Relation lRelation = Relation.findByPK(100306); // this is just an EM.find(Relation.class, relationnr);
lRelation.setName(lRelation.getName() + "x");

Address lAddress = lRelation.getAddressesWhereIAmRelation().get(0);
lRelation.removeAddressesWhereIAmRelation(lAddress); // removes from collection and sets relation to null
lEntityManager.remove(lAddress);

lEntityManager.getTransaction().begin();
lEntityManager.merge(lRelation);
lEntityManager.getTransaction().commit();

I see that first an update is executed setting the relationnr to null

setBigDecimal=16034 / UPDATE address SET dwhmodified = ?, dwhby = ?, relationnr = ?, lazylock = ? WHERE ((addressnr = >>>HERE<<< ) AND (lazylock = ?))
setNull=-5 / UPDATE address SET dwhmodified = ?, relationnr = >>>HERE<<< , lazylock = ? WHERE ((addressnr = ?) AND (lazylock = ?))

And then immediately a delete

setBigDecimal=16034 / DELETE FROM address WHERE ((addressnr = >>>HERE<<< ) AND (lazylock = ?))

Why update then delete? There is no cascade merge...

@ManyToOne(fetch = FetchType.LAZY, targetEntity = nl.reinders.bm.Relation.class, cascade = {CascadeType.REFRESH} ) @JoinColumn(name="relationnr")
volatile protected nl.reinders.bm.Relation iRelation;

@OneToMany(mappedBy = "iRelation", fetch = FetchType.LAZY, targetEntity = nl.reinders.bm.Address.class, cascade = {CascadeType.REFRESH,CascadeType.REMOVE} )
volatile protected java.util.List<nl.reinders.bm.Address> iAddressesWhereIAmRelation = new java.util.ArrayList<nl.reinders.bm.Address>();

Tom
Re: new events [message #503749 is a reply to message #503037] Tue, 15 December 2009 15:46 Go to previous messageGo to next message
James Sutherland is currently offline James SutherlandFriend
Messages: 1939
Registered: July 2009
Location: Ottawa, Canada
Senior Member

The code is a little odd. Since you appear to be finding the lRelation in the same EntityManager, there is no point to calling merge() on it, as it is already managed. Also it is somewhat odd to be calling remove() before starting the transaction.

The UPDATE occurs before the DELETE because you are modifying the object, if you did not modify it then it would not be updated. Objects that are modified are updated before they are deleted because the update may be required for database constraints.

If you do not want the update, then don't modify the object. EclipseLink does also offer a performDeleteFirst option, but I would not recommend that. You could also refresh() it before calling remove() to clear your changes (or revert it in the UnitOfWork API), but I would recommend not modifying it if you do not want it updated.




James : Wiki : Book : Blog : Twitter
Re: new events [message #503762 is a reply to message #503749] Tue, 15 December 2009 16:03 Go to previous message
Tom Eugelink is currently offline Tom EugelinkFriend
Messages: 817
Registered: July 2009
Senior Member
On 2009-12-15 16:46, James wrote:
> The code is a little odd. Since you appear to be finding the lRelation
> in the same EntityManager, there is no point to calling merge() on it,
> as it is already managed. Also it is somewhat odd to be calling remove()
> before starting the transaction.

Yes. But consider this from a stand alone Swing application point of view; you are updating the entities all around the swing application, but I only start a transaction and manipulate the database when the users presses "save". Until then everything must be done in memory. So that is the reason for the remove-out-side-the-transaction; simulate a user deleting a child (address).

This is also why I have an extended entity manager, which remembers any actions done when no transaction is active, and then re-executes those upon the commit.


> The UPDATE occurs before the DELETE because you are modifying the
> object, if you did not modify it then it would not be updated. Objects
> that are modified are updated before they are deleted because the update
> may be required for database constraints.

Ok. That makes things clear; this is the result of the "setModifiedBy" call I mention in another post.

Tom
Previous Topic:Cache Coordination Architecture (RMI & multicast)
Next Topic:How to implement preUpdate from DescriptorEventAdapter?
Goto Forum:
  


Current Time: Tue Mar 19 06:52:09 GMT 2024

Powered by FUDForum. Page generated in 0.02837 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top