You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pc...@apache.org on 2008/06/12 01:14:25 UTC

svn commit: r666888 - in /openjpa/trunk: openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/ openjpa-persistence-jdbc/src/test/jav...

Author: pcl
Date: Wed Jun 11 16:14:24 2008
New Revision: 666888

URL: http://svn.apache.org/viewvc?rev=666888&view=rev
Log:
OPENJPA-245. Backported r646082 to trunk.

Added:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/TestAttachConstructedCopy.java
      - copied unchanged from r646082, openjpa/branches/1.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/TestAttachConstructedCopy.java
Modified:
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionAttachStrategy.java
    openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties
    openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_remote.xml

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java?rev=666888&r1=666887&r2=666888&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java Wed Jun 11 16:14:24 2008
@@ -118,7 +118,10 @@
     public static final int ENHANCE_INTERFACE = 2 << 1;
     public static final int ENHANCE_PC = 2 << 2;
 
-    private static final String PRE = "pc";
+    public static final String PRE = "pc";
+    public static final String ISDETACHEDSTATEDEFINITIVE = PRE 
+        + "isDetachedStateDefinitive";
+
     private static final Class PCTYPE = PersistenceCapable.class;
     private static final String SM = PRE + "StateManager";
     private static final Class SMTYPE = StateManager.class;
@@ -2999,12 +3002,27 @@
      */
     private void addIsDetachedMethod()
         throws NoSuchMethodException {
-        // public boolean pcIsDetached ()
+        // public boolean pcIsDetached()
         BCMethod method = _pc.declareMethod(PRE + "IsDetached",
             Boolean.class, null);
         method.makePublic();
         Code code = method.getCode(true);
-        writeIsDetachedMethod(code);
+        boolean needsDefinitiveMethod = writeIsDetachedMethod(code);
+        code.calculateMaxStack();
+        code.calculateMaxLocals();
+        if (!needsDefinitiveMethod) 
+            return;
+
+        // private boolean pcIsDetachedStateDefinitive()
+        //   return false;
+        // auxilliary enhancers may change the return value of this method
+        // if their specs consider detached state definitive
+        method = _pc.declareMethod(ISDETACHEDSTATEDEFINITIVE, boolean.class,
+            null);
+        method.makePrivate();
+        code = method.getCode(true);
+        code.constant().setValue(false);
+        code.ireturn();
         code.calculateMaxStack();
         code.calculateMaxLocals();
     }
@@ -3012,14 +3030,17 @@
     /**
      * Creates the body of the pcIsDetached() method to determine if an
      * instance is detached.
+     *
+     * @return true if we need a pcIsDetachedStateDefinitive method, false
+     * otherwise
      */
-    private void writeIsDetachedMethod(Code code)
+    private boolean writeIsDetachedMethod(Code code)
         throws NoSuchMethodException {
         // not detachable: return Boolean.FALSE
         if (!_meta.isDetachable()) {
             code.getstatic().setField(Boolean.class, "FALSE", Boolean.class);
             code.areturn();
-            return;
+            return false;
         }
 
         // if (sm != null)
@@ -3067,7 +3088,7 @@
                 ifins.setTarget(target);
                 notdeser.setTarget(target);
                 code.areturn();
-                return;
+                return false;
             }
         }
 
@@ -3077,9 +3098,9 @@
         if (notdeser != null)
             notdeser.setTarget(target);
 
-        // allow users with version fields to manually construct a "detached"
-        // instance, so check version before taking into account non-existent
-        // detached state
+        // allow users with version or auto-assigned pk fields to manually 
+        // construct a "detached" instance, so check these before taking into 
+        // account non-existent detached state
 
         // consider detached if version is non-default
         FieldMetaData version = _meta.getVersionField();
@@ -3094,41 +3115,13 @@
             ifins.setTarget(code.getstatic().setField(Boolean.class, "FALSE",
                 Boolean.class));
             code.areturn();
-            return;
-        }
-
-        // no detached state: if instance uses detached state and it's not
-        // synthetic or the instance is not serializable or the state isn't
-        // transient, must not be detached
-        if (state == null
-            && (!ClassMetaData.SYNTHETIC.equals(_meta.getDetachedState())
-            || !Serializable.class.isAssignableFrom(_meta.getDescribedType())
-            || !_repos.getConfiguration().getDetachStateInstance().
-            isDetachedStateTransient())) {
-            // return Boolean.FALSE
-            code.getstatic().setField(Boolean.class, "FALSE", Boolean.class);
-            code.areturn();
-            return;
-        }
-
-        // no detached state: if instance uses detached state (and must be
-        // synthetic and transient in serializable instance at this point),
-        // not detached if state not set to DESERIALIZED
-        if (state == null) {
-            // if (pcGetDetachedState () == null) // instead of DESERIALIZED
-            //     return Boolean.FALSE;
-            loadManagedInstance(code, false);
-            code.invokevirtual().setMethod(PRE + "GetDetachedState",
-                Object.class, null);
-            ifins = code.ifnonnull();
-            code.getstatic().setField(Boolean.class, "FALSE", Boolean.class);
-            code.areturn();
-            ifins.setTarget(code.nop());
+            return false;
         }
 
         // consider detached if auto-genned primary keys are non-default
         ifins = null;
         JumpInstruction ifins2 = null;
+        boolean hasAutoAssignedPK = false;
         if (state != Boolean.TRUE
             && _meta.getIdentityType() == ClassMetaData.ID_APPLICATION) {
             // for each pk field:
@@ -3162,13 +3155,65 @@
             }
         }
 
-        // give up; we just don't know
-        target = code.constant().setNull();
+        // create artificial target to simplify
+        target = code.nop();
         if (ifins != null)
             ifins.setTarget(target);
         if (ifins2 != null)
             ifins2.setTarget(target);
+
+        // if has auto-assigned pk and we get to this point, must have default
+        // value, so must be new instance
+        if (hasAutoAssignedPK) {
+            code.getstatic().setField(Boolean.class, "FALSE", Boolean.class);
+            code.areturn();
+            return false;
+        }
+
+        // if detached state is not definitive, just give up now and return
+        // null so that the runtime will perform a DB lookup to determine
+        // whether we're detached or new
+        code.aload().setThis();
+        code.invokespecial().setMethod(ISDETACHEDSTATEDEFINITIVE, boolean.class,
+            null);
+        ifins = code.ifne();
+        code.constant().setNull();
         code.areturn();
+        ifins.setTarget(code.nop());
+
+        // no detached state: if instance uses detached state and it's not
+        // synthetic or the instance is not serializable or the state isn't
+        // transient, must not be detached
+        if (state == null
+            && (!ClassMetaData.SYNTHETIC.equals(_meta.getDetachedState())
+            || !Serializable.class.isAssignableFrom(_meta.getDescribedType())
+            || !_repos.getConfiguration().getDetachStateInstance().
+            isDetachedStateTransient())) {
+            // return Boolean.FALSE
+            code.getstatic().setField(Boolean.class, "FALSE", Boolean.class);
+            code.areturn();
+            return true;
+        }
+
+        // no detached state: if instance uses detached state (and must be
+        // synthetic and transient in serializable instance at this point),
+        // not detached if state not set to DESERIALIZED
+        if (state == null) {
+            // if (pcGetDetachedState () == null) // instead of DESERIALIZED
+            //     return Boolean.FALSE;
+            loadManagedInstance(code, false);
+            code.invokevirtual().setMethod(PRE + "GetDetachedState",
+                Object.class, null);
+            ifins = code.ifnonnull();
+            code.getstatic().setField(Boolean.class, "FALSE", Boolean.class);
+            code.areturn();
+            ifins.setTarget(code.nop());
+        }
+
+        // give up; we just don't know
+        code.constant().setNull();
+        code.areturn();
+        return true;
     }
 
     /**

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionAttachStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionAttachStrategy.java?rev=666888&r1=666887&r2=666888&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionAttachStrategy.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionAttachStrategy.java Wed Jun 11 16:14:24 2008
@@ -69,19 +69,12 @@
     public Object attach(AttachManager manager, Object toAttach,
         ClassMetaData meta, PersistenceCapable into, OpenJPAStateManager owner,
         ValueMetaData ownerMeta, boolean explicit) {
-
-        // VersionAttachStrategy is invoked in the case where no more
-        // intelligent strategy could be found; let's be more lenient
-        // about new vs. detached record determination.
-        if (into == null)
-            into = findFromDatabase(manager, toAttach);
-
         BrokerImpl broker = manager.getBroker();
         PersistenceCapable pc = ImplHelper.toPersistenceCapable(toAttach,
             meta.getRepository().getConfiguration());
 
         boolean embedded = ownerMeta != null && ownerMeta.isEmbeddedPC();
-        boolean isNew = !broker.isDetached(pc) && into == null;
+        boolean isNew = !broker.isDetached(pc);
         Object version = null;
         StateManagerImpl sm;
 

Modified: openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties?rev=666888&r1=666887&r2=666888&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties (original)
+++ openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties Wed Jun 11 16:14:24 2008
@@ -116,7 +116,9 @@
 trans-not-managed: This broker is not configured to use managed transactions.
 bad-detached-op: You cannot perform operation {0} on detached object "{1}". \
 	This operation only applies to managed objects.
-persist-detached: Attempt to persist detached object "{0}".
+persist-detached: Attempt to persist detached object "{0}".  If this is a new \
+  instance, make sure any versino and/or auto-generated primary key fields are \
+  null/default when persisting.
 null-value: The field "{0}" of instance "{1}" contained a null value; \
 	the metadata for this field specifies that nulls are illegal.
 change-identity: Attempt to change a primary key field of an instance that \
@@ -396,4 +398,4 @@
     an active datastore (pessimistic) transaction.
 cant-serialize-connected-broker: Serialization not allowed for brokers with \
     an active connection to the database.
-no-interface-metadata: No metadata was found for managed interface {0}.
\ No newline at end of file
+no-interface-metadata: No metadata was found for managed interface {0}.

Modified: openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_remote.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_remote.xml?rev=666888&r1=666887&r2=666888&view=diff
==============================================================================
--- openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_remote.xml (original)
+++ openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_remote.xml Wed Jun 11 16:14:24 2008
@@ -195,7 +195,10 @@
                     <para>
 If the instance has a <literal>Version</literal> field,
 OpenJPA will consider the object detached if the version field has a non-default
-value, and new otherwise.
+value, and new otherwise.  Similarly, if the instance has 
+<literal>GeneratedValue</literal> primary key fields, OpenJPA will consider the
+object detached if any of these fields have non-default values, and new 
+otherwise.
                     </para>
                     <para>
 When attaching null fields in these cases, OpenJPA cannot distinguish between a