The Source for Java Technology Collaboration

Home » java.net Forums » GlassFish » GlassFish

Thread: Problem with merge()

Welcome, Guest Help
Login Login
Guest Settings Guest Settings
Reply to this Thread Reply to this Thread Search Forum Search Forum Back to Thread List Back to Thread List

Permlink Replies: 4 - Last Post: Feb 23, 2006 7:28 PM by: isana
dibyendumajumdar

Posts: 55
Problem with merge()
Posted: Feb 21, 2006 8:36 AM
  Click to reply to this thread Reply

I am posting this again as a separate thread

I have a simple web application that allows users to add, modify Warehouses. I am using persist() for adding new instances, and merge() for updating existing instances. However, when I invoke merge, I get an error because the EntityManager attempts to use INSERT statement rather than UPDATE.

My web application is using a Data Access Layer that is implemented as EJB Session Bean. The update cycle is as follows:

1. Get a list of Warehouses in the system.
2. Allow user to pick one for updating.
3. Display selected Warehouse and allow user to modify it.
4. When the user saves the updated warehouse, persist it using a call to merge(). The merge call is in the Data Access Layer, of course.

Initially, I was not using the original Warehouse object returned by the Data Access Layer - I was creating a new instance and then trying to merge it. This caused an Optimistlic locking exception. I then changed the code to use the Warehouse object returned by the Data Access Layer and updated this. Now I am getting an Exception because the EntityManager is attempting to INSERT the data rather than UPDATE it.

Can someone tell me if I am missing something somewhere.

I think that it is a mistake to have two interfaces that are overlapping in functionality. I think that persist() should always do an INSERT and merge() should always do an UPDATE. As it stands now, the behaviour is unpredictable and I think will be a source of confusion to developers.

Regards

Dibyendu

gyorke

Posts: 208
Re: Problem with merge()
Posted: Feb 23, 2006 8:46 AM   in response to: dibyendumajumdar
  Click to reply to this thread Reply

The confusion can be cleared a little when thinking of the mentioned APIs as operations performed on the object model. They are not Database operations.
Persist tells the Persistence Framework that the user knows the object in question is new. Merge however is more of a convenience method for integrating changes from a detached tree into a managed tree. The Persistence Provider will update the pre-existing objects and insert the new ones automatically.

The issue you are seeing is because the TopLink Persistence Provider has a default existence check that checks in memory for existence. Checking in memory works for majority of applications and is much more performant than querying the database for existence. In a future build of the TopLink Persistence Provider simple configuration will be available to override the existence checking to have TopLink check the database for existence.

In the meantime if you were to read the Warehouse object through the EntityManager instead of creating a new instance of Warehouse the duplicate PK exception will not occur.

--Gordon

dibyendumajumdar

Posts: 55
Re: Problem with merge()
Posted: Feb 23, 2006 2:52 PM   in response to: gyorke
  Click to reply to this thread Reply

> Persist tells the Persistence Framework that the user
> knows the object in question is new. Merge however
> is more of a convenience method for integrating
> changes from a detached tree into a managed tree.
> The Persistence Provider will update the
> e pre-existing objects and insert the new ones
> automatically.

Thank you for the explanation - why can't the spec state this clearly?

> The issue you are seeing is because the TopLink
> nk Persistence Provider has a default existence check
> that checks in memory for existence. Checking in
> memory works for majority of applications and is much
> more performant than querying the database for
> existence. In a future build of the TopLink
> Persistence Provider simple configuration will be
> available to override the existence checking to have
> TopLink check the database for existence.
>
> In the meantime if you were to read the Warehouse
> object through the EntityManager instead of creating
> a new instance of Warehouse the duplicate PK
> exception will not occur.

Well, I am doing that as a workaround. For instance:

public void updateWarehouse(Warehouse wh) {
if (entityManager.find(Warehouse.class, wh.getId()) == null) {
throw new RuntimeException("Unable to update " + wh + " because there is no prior version");
}
entityManager.merge(wh);
}

But, I still feel that it would be better for merge() to always assume that an UPDATE is required. It can then check in memory, and if the entities aren't found, it can automatically check the existence of the entities in the database. The current approach of trying to figure out which bits are new and which bits are to be updated is not foolproof. Using configuration to override memory based checking will only make the system unnecessarily inefficient.

An interesting case is when there are related entities. For instance, Warehouses have a one-to-many relationship with Districts. I am using the following technique to work around the persist/merge issues:

1. I cascade MERGE and REFRESH but not PERSIST.

2. I call persist when I know the object is new. Both when creating a Warehouse and when creating a District. Since I do not cascade PERSIST, this works okay, otherwise, the persist of District would fail if the Entity Manager decided to try to INSERT a new Warehouse object.

3. I call merge when I am updating the Entity but call find() first, as shown above, to refresh the Entity. I rely upon cascading REFRESH to ensure that the related entities are also fetched. This ensures that the merge() call will do the right thing.

I do wish that this was much easier.

Regards

Dibyendu

dibyendumajumdar

Posts: 55
Re: Problem with merge()
Posted: Feb 23, 2006 3:19 PM   in response to: dibyendumajumdar
  Click to reply to this thread Reply

Okay, I can see what the motivation for current behaviour of merge() is. It works well in the following situation:

1. Read the entity hierarchy into memory.
2. Allow user to update the hierarchy in memory.
3. Merge back the changes. The EntityManager works out which bits are to be updated and which bits are new.

This is great except that steps 1 and 3 may not be in the same transaction context (as in my case) and then the merge doesn't work.

I can see two ways around this:

1. Use a stateful session bean and extended transaction contexts. I don't like this because it impacts scalability of the system.

2. If a stateless bean is being used, and the transaction contexts for 1 and 3 and different, the workaround is to refresh the root of the entity hierarchy prior to the call to merge(). The refresh should recursively refresh the entire tree.

Am I correct?

Regards

Dibyendu

isana

Posts: 98
Re: Problem with merge()
Posted: Feb 23, 2006 7:28 PM   in response to: dibyendumajumdar
  Click to reply to this thread Reply

Hello,

I'm having the same trouble and I cannot use entities over reboot of GlassFish or redeployment of the EAR. After that, all entities are regarded as new in merge().

As I posted to the issue#123 (https://glassfish.dev.java.net/issues/show_bug.cgi?id=123), this problem does NOT occur in persistence operations outside GlassFish...

Regards,
- Ryosuke.




 XML java.net RSS