openjpa-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Rick Curtis (JIRA)" <>
Subject [jira] Commented: (OPENJPA-1896) OpenJPA cannot store POJOs if a corresponding record already exists
Date Wed, 22 Dec 2010 19:18:01 GMT


Rick Curtis commented on OPENJPA-1896:

This required two changes to get everything working :

1.] I updated the enhancer (PCEnhancer) so that when we are writing pcIsDetached() if we can't
make a definitive answer, to return null. When merging an Entity and we don't know if it is
detached, we will query for the Entity to see if we need to issue an UPDATE or an INSERT.

2.] *Note - This is the part that I'm not very excited about.... 

This change is required for an Entity which has a primitive @Version field. After applying
fix [1] we now query the DB to see if these Entities exist. Now that we know that we're going
to need to do an update, we look at the version field to see if the value is different than
those in the DB. In the case of a non-primitive version field, it will be null and we know
that it wasn't set. 

In the case of a primitive field, it will be initialized to the types default value. If the
current version is greater than the default for the type we will incorrectly throw on optimistic
lock exception. To fix the problem I was able to peer inside the Entity and check the value
of the pcVersionInit field(added by the Enhancer to detect whether the version field was set,
or if it is a default value). Unfortunately the value of this field isn't exposed on the PersistenceCapeable
interface, so I had to use reflection.

After thinking some more about this, I have an edge case that I can't fix...

Lets say I have an Entity with an int version column and for whatever reason the value is
0. Now we find an Entity and stream it out to client 1. Next another client modifies that
row so the version gets incremented to 1. Client 1 updates that data and sends it back to
the server. At this time we have no state manager and we try to merge it back into the persistence
context. This is the problem. We *should* get an OLE here because the current Entity has a
version of 0 but the DB has a version of 1. We don't get an OLE because we will assume that
the value of the version column is a defaulted value, not one that was actually set there.
Make sense?

The net of this is that we should tell users to either make sure their version columns start
with a value greater than 0, or to use non primitive types.

> OpenJPA cannot store POJOs if a corresponding record already exists
> -------------------------------------------------------------------
>                 Key: OPENJPA-1896
>                 URL:
>             Project: OpenJPA
>          Issue Type: Bug
>    Affects Versions: 2.0.1
>         Environment: Eclipse, Sun Java 1.6, Ubuntu Lucid, Guice JPAPersistModule, build-time
>            Reporter: Cefn Hoile
>            Assignee: Rick Curtis
>             Fix For: 1.0.5, 2.1.0, 2.2.0
>         Attachments: openjpa-1896.jar
> If a POJO is created using a java constructor, merge() cannot store the newly constructed
object's data if this means updating a pre-existing record with a matching identity.
> This is a major bug since it means applications where the objects have a natural key
cannot use OpenJPA. In my case the example was a filesystem; each crawl of the filesystem
generates its own data objects with file path as the natural key. These objects then need
to be stored into the database. Previous crawls may have encountered the same files, and the
merge operation should cause the latest data from the POJO to be stored in the pre-existing
> Instead, any attempt to execute either merge() or persist() on an independently constructed
object with a matching record identity in the database triggers the same error in the database
layer, since OpenJPA attempts to execute an insert for a pre-existing primary key, throwing...
> org.apache.openjpa.lib.jdbc.ReportingSQLException: ERROR: duplicate key value violates
unique constraint "file_pkey" {prepstmnt 32879825 INSERT INTO file (locationString, location,
version, folder) VALUES (?, ?, ?, ?)  [params=?, ?, ?, ?]} [code=0, state=23505]
> From discussion with Rick Curtis on the list, this is because
the version field on a POJO which is unmanaged is not yet set. 
> An ASSUMPTION seems to be made that no such record exists in the database already since
it wasn't loaded from the database in the first place, so a persist is attempted. Instead,
I recommend the database is QUERIED TO FIND OUT if such a record already exists, and the version
field is set correspondingly before attempting the merge() 
> Here is the corresponding thread containing Ricks comments and links to an example in
Github which can recreate the problem.

This message is automatically generated by JIRA.
You can reply to this email to add a comment to the issue online.

View raw message