[ 
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.

Reply via email to