[ https://issues.apache.org/jira/browse/OPENJPA-245?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#action_12499951 ]
Pinaki Poddar commented on OPENJPA-245: --------------------------------------- >From openjpa documentation (ref: 1.2. Attach Behavior) " * If the instance was detached and detached state is enabled, OpenJPA will use the detached state to determine the object's version and primary key values. In addition, this state will tell OpenJPA which fields were loaded at the time of detach, and in turn where to expect changes. Loaded detached fields with null values will set the attached instance's corresponding fields to null. * If the instance has a Version field, OpenJPA will consider the object detached if the version field has a non-default value, and new otherwise. * If neither of the above cases apply, OpenJPA will check to see if an instance with the same primary key values exists in the database. If so, the object is considered detached. Otherwise, it is considered new." The implementaion does not seem to adhere to the last statement above. This is observed by the original scenario described in this issue as well as the attached testcase testMergeUnversionedNewObjectCreatesNewRecord(). AttachStrategy implementaion is responsible for merge(). For a newly created entity instance even if it carries id of a persistent record -- the attach strategy selected (by AttachManager.getStrategy() method) is VersionAttachStrategy. Now, VersionAttachStrategy determines whether the instance to be attached is new by the following logic: boolean isNew = !broker.isDetached(pc); // VersionAttachStrategy.java:70 which turns out to be true in this case. With the current implementation, VersionAttachStrategy does not detect that a) the instance to be attached is carrying a primary key equal to a pre-existing data record and b) hence it should be an update and not an insert. However, it passes the instance as PNEW further downstream to be flushed (the instance is still carrying a primary key value same as the one set by the application and a record with the same key exists). However, as the identity field is annotated with GenerateValue.IDENTITY -- the PNEW instance gets stored without a duplicate key exception and its primary key in the database is auto incremented. That the primary key value assigned by the user application is ignored and a new value is assigned can also be verified (see the testcase). This is the flow for a new instance being merged in a context which is not managing another instance with the same primary key. If the new instance carrying an existing key was merged to a EntityManager that already is managing another instance, because the attach strategy still considered the to be merged instance as PNEW -- a EntityExistsException is raised by the object management kernel even before attampting a flush to the database. > Attach NEW and auto-increment identity > -------------------------------------- > > Key: OPENJPA-245 > URL: https://issues.apache.org/jira/browse/OPENJPA-245 > Project: OpenJPA > Issue Type: Bug > Components: jpa > Affects Versions: 0.9.6, 0.9.7 > Environment: jdk1.5.0_11, Win XP, Fedora Core 6, Postgres 8.1 (on > Fedora) > Reporter: Aleksandar Likic > Attachments: TestMerge.zip > > > According to documentation (1.2 Attach Behavior), when an entity instance is > NEW (never detached): > * If neither of the above cases apply, OpenJPA will check to see if an > instance with the same primary key values exists in the database. If so, the > object is considered detached. Otherwise, it is considered new. > This doesn't work for me - a new record in database is created on commit > instead of updating the existing one. The "regular" case - > detach/modify/attach works fine - the existing record is updated. > It is very easy to reproduce - just create a new instance of an entity, > assign an already existing primary key, call em.merge() and commit. A new > record will be created in database, with new, auto-generated primary key. > I stumbled on this trying to implement a web service that uses OpenJPA-based > backend. When servicing an "update" request, the web service instantiates a > NEW object (by performing XML de-serialization) and calls em.merge to update > the entity. A new record gets created instead of updating an existing one. > ------------ Entity class (START) ------------------------------ > package exaple; > public class Consumer implements java.io.Serializable { > private long id; > public long getId() { > return this.id; > } > public void setId(long id) { > this.id = id; > } > private java.lang.String firstName; > public java.lang.String getFirstName() { > return this.firstName; > } > public void setFirstName(java.lang.String firstName) { > this.firstName = firstName; > } > private java.lang.String lastName; > public java.lang.String getLastName() { > return this.lastName; > } > public void setLastName(java.lang.String lastName) { > this.lastName = lastName; > } > ------------ Entity class (END) ------------------------------ > ------------ persistence.xml (START) ------------------------------ > <?xml version="1.0" encoding="UTF-8"?> > <persistence xmlns="http://java.sun.com/xml/ns/persistence" > xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0"> > <persistence-unit name="example" transaction-type="RESOURCE_LOCAL"> > > > <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider> > <!-- We must enumerate each entity in the persistence unit --> > <class>example.Consumer</class> > <properties> > <property name="openjpa.jdbc.DBDictionary" value="postgres"/> > <property name="openjpa.ConnectionDriverName" > value="org.postgresql.Driver"/> > <property name="openjpa.ConnectionUserName" value="app_user"/> > <property name="openjpa.ConnectionPassword" value="app_user"/> > <property name="openjpa.ConnectionURL" > value="jdbc:postgresql://localhost/alikic"/> > <property name="openjpa.Log" value="DefaultLevel=WARN,SQL=TRACE"/> > > </properties> > </persistence-unit> > > </persistence> > ------------ persistence.xml (END) ------------------------------ > ------------ orm.xml (START) ------------------------------ > <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" > xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm > orm_1_0.xsd" > version="1.0"> > <entity class="example.Consumer"> > <attributes> > <id name="id"> > <generated-value strategy="IDENTITY"/> > </id> > <basic name="firstName"> > <column name="first_name"/> > </basic> > <basic name="lastName"> > <column name="last_name"/> > </basic> > </attributes> > </entity> > </entity-mappings> > ------------ orm.xml (END) ------------------------------ -- This message is automatically generated by JIRA. - You can reply to this email to add a comment to the issue online.