mirror of https://github.com/apache/openjpa.git
More efficient fix for OPENJPA-245.
git-svn-id: https://svn.apache.org/repos/asf/openjpa/branches/1.1.x@646082 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
a82b7332f1
commit
7cece296b3
|
@ -118,7 +118,10 @@ public class PCEnhancer {
|
||||||
public static final int ENHANCE_INTERFACE = 2 << 1;
|
public static final int ENHANCE_INTERFACE = 2 << 1;
|
||||||
public static final int ENHANCE_PC = 2 << 2;
|
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 Class PCTYPE = PersistenceCapable.class;
|
||||||
private static final String SM = PRE + "StateManager";
|
private static final String SM = PRE + "StateManager";
|
||||||
private static final Class SMTYPE = StateManager.class;
|
private static final Class SMTYPE = StateManager.class;
|
||||||
|
@ -2999,12 +3002,27 @@ public class PCEnhancer {
|
||||||
*/
|
*/
|
||||||
private void addIsDetachedMethod()
|
private void addIsDetachedMethod()
|
||||||
throws NoSuchMethodException {
|
throws NoSuchMethodException {
|
||||||
// public boolean pcIsDetached ()
|
// public boolean pcIsDetached()
|
||||||
BCMethod method = _pc.declareMethod(PRE + "IsDetached",
|
BCMethod method = _pc.declareMethod(PRE + "IsDetached",
|
||||||
Boolean.class, null);
|
Boolean.class, null);
|
||||||
method.makePublic();
|
method.makePublic();
|
||||||
Code code = method.getCode(true);
|
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.calculateMaxStack();
|
||||||
code.calculateMaxLocals();
|
code.calculateMaxLocals();
|
||||||
}
|
}
|
||||||
|
@ -3012,14 +3030,17 @@ public class PCEnhancer {
|
||||||
/**
|
/**
|
||||||
* Creates the body of the pcIsDetached() method to determine if an
|
* Creates the body of the pcIsDetached() method to determine if an
|
||||||
* instance is detached.
|
* 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 {
|
throws NoSuchMethodException {
|
||||||
// not detachable: return Boolean.FALSE
|
// not detachable: return Boolean.FALSE
|
||||||
if (!_meta.isDetachable()) {
|
if (!_meta.isDetachable()) {
|
||||||
code.getstatic().setField(Boolean.class, "FALSE", Boolean.class);
|
code.getstatic().setField(Boolean.class, "FALSE", Boolean.class);
|
||||||
code.areturn();
|
code.areturn();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (sm != null)
|
// if (sm != null)
|
||||||
|
@ -3067,7 +3088,7 @@ public class PCEnhancer {
|
||||||
ifins.setTarget(target);
|
ifins.setTarget(target);
|
||||||
notdeser.setTarget(target);
|
notdeser.setTarget(target);
|
||||||
code.areturn();
|
code.areturn();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3077,9 +3098,9 @@ public class PCEnhancer {
|
||||||
if (notdeser != null)
|
if (notdeser != null)
|
||||||
notdeser.setTarget(target);
|
notdeser.setTarget(target);
|
||||||
|
|
||||||
// allow users with version fields to manually construct a "detached"
|
// allow users with version or auto-assigned pk fields to manually
|
||||||
// instance, so check version before taking into account non-existent
|
// construct a "detached" instance, so check these before taking into
|
||||||
// detached state
|
// account non-existent detached state
|
||||||
|
|
||||||
// consider detached if version is non-default
|
// consider detached if version is non-default
|
||||||
FieldMetaData version = _meta.getVersionField();
|
FieldMetaData version = _meta.getVersionField();
|
||||||
|
@ -3094,41 +3115,13 @@ public class PCEnhancer {
|
||||||
ifins.setTarget(code.getstatic().setField(Boolean.class, "FALSE",
|
ifins.setTarget(code.getstatic().setField(Boolean.class, "FALSE",
|
||||||
Boolean.class));
|
Boolean.class));
|
||||||
code.areturn();
|
code.areturn();
|
||||||
return;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// 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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// consider detached if auto-genned primary keys are non-default
|
// consider detached if auto-genned primary keys are non-default
|
||||||
ifins = null;
|
ifins = null;
|
||||||
JumpInstruction ifins2 = null;
|
JumpInstruction ifins2 = null;
|
||||||
|
boolean hasAutoAssignedPK = false;
|
||||||
if (state != Boolean.TRUE
|
if (state != Boolean.TRUE
|
||||||
&& _meta.getIdentityType() == ClassMetaData.ID_APPLICATION) {
|
&& _meta.getIdentityType() == ClassMetaData.ID_APPLICATION) {
|
||||||
// for each pk field:
|
// for each pk field:
|
||||||
|
@ -3162,13 +3155,65 @@ public class PCEnhancer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// give up; we just don't know
|
// create artificial target to simplify
|
||||||
target = code.constant().setNull();
|
target = code.nop();
|
||||||
if (ifins != null)
|
if (ifins != null)
|
||||||
ifins.setTarget(target);
|
ifins.setTarget(target);
|
||||||
if (ifins2 != null)
|
if (ifins2 != null)
|
||||||
ifins2.setTarget(target);
|
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();
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -69,19 +69,12 @@ class VersionAttachStrategy
|
||||||
public Object attach(AttachManager manager, Object toAttach,
|
public Object attach(AttachManager manager, Object toAttach,
|
||||||
ClassMetaData meta, PersistenceCapable into, OpenJPAStateManager owner,
|
ClassMetaData meta, PersistenceCapable into, OpenJPAStateManager owner,
|
||||||
ValueMetaData ownerMeta, boolean explicit) {
|
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();
|
BrokerImpl broker = manager.getBroker();
|
||||||
PersistenceCapable pc = ImplHelper.toPersistenceCapable(toAttach,
|
PersistenceCapable pc = ImplHelper.toPersistenceCapable(toAttach,
|
||||||
meta.getRepository().getConfiguration());
|
meta.getRepository().getConfiguration());
|
||||||
|
|
||||||
boolean embedded = ownerMeta != null && ownerMeta.isEmbeddedPC();
|
boolean embedded = ownerMeta != null && ownerMeta.isEmbeddedPC();
|
||||||
boolean isNew = !broker.isDetached(pc) && into == null;
|
boolean isNew = !broker.isDetached(pc);
|
||||||
Object version = null;
|
Object version = null;
|
||||||
StateManagerImpl sm;
|
StateManagerImpl sm;
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,9 @@ not-managed: The given instance "{0}" is not managed by this context.
|
||||||
trans-not-managed: This broker is not configured to use managed transactions.
|
trans-not-managed: This broker is not configured to use managed transactions.
|
||||||
bad-detached-op: You cannot perform operation {0} on detached object "{1}". \
|
bad-detached-op: You cannot perform operation {0} on detached object "{1}". \
|
||||||
This operation only applies to managed objects.
|
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; \
|
null-value: The field "{0}" of instance "{1}" contained a null value; \
|
||||||
the metadata for this field specifies that nulls are illegal.
|
the metadata for this field specifies that nulls are illegal.
|
||||||
change-identity: Attempt to change a primary key field of an instance that \
|
change-identity: Attempt to change a primary key field of an instance that \
|
||||||
|
@ -396,4 +398,4 @@ cant-serialize-pessimistic-broker: Serialization not allowed for brokers with \
|
||||||
an active datastore (pessimistic) transaction.
|
an active datastore (pessimistic) transaction.
|
||||||
cant-serialize-connected-broker: Serialization not allowed for brokers with \
|
cant-serialize-connected-broker: Serialization not allowed for brokers with \
|
||||||
an active connection to the database.
|
an active connection to the database.
|
||||||
no-interface-metadata: No metadata was found for managed interface {0}.
|
no-interface-metadata: No metadata was found for managed interface {0}.
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.openjpa.persistence.detachment;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
|
||||||
|
import junit.textui.TestRunner;
|
||||||
|
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that manually constructing instances with existing primary key values
|
||||||
|
* and attaching them works.
|
||||||
|
*
|
||||||
|
* @author Abe White
|
||||||
|
*/
|
||||||
|
public class TestAttachConstructedCopy
|
||||||
|
extends SingleEMFTestCase {
|
||||||
|
|
||||||
|
public void setUp() {
|
||||||
|
setUp(Record.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAttachConstructedCopyWithGeneratedPKAndNoVersion() {
|
||||||
|
Record record = new Record();
|
||||||
|
record.setContent("orig");
|
||||||
|
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
em.persist(record);
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
int id = record.getId();
|
||||||
|
|
||||||
|
Record copy = new Record();
|
||||||
|
copy.setId(id);
|
||||||
|
copy.setContent("new");
|
||||||
|
|
||||||
|
em = emf.createEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
record = em.merge(copy);
|
||||||
|
assertTrue(record != copy);
|
||||||
|
assertEquals("new", record.getContent());
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
|
||||||
|
em = emf.createEntityManager();
|
||||||
|
record = em.find(Record.class, id);
|
||||||
|
assertEquals("new", record.getContent());
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
TestRunner.run(TestAttachConstructedCopy.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -173,7 +173,10 @@ attached instance's corresponding fields to null.
|
||||||
<para>
|
<para>
|
||||||
If the instance has a <literal>Version</literal> field,
|
If the instance has a <literal>Version</literal> field,
|
||||||
OpenJPA will consider the object detached if the version field has a non-default
|
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>
|
||||||
<para>
|
<para>
|
||||||
When attaching null fields in these cases, OpenJPA cannot distinguish between a
|
When attaching null fields in these cases, OpenJPA cannot distinguish between a
|
||||||
|
|
Loading…
Reference in New Issue