diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/EmbedFieldStrategy.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/EmbedFieldStrategy.java index 1a3caa419..dad32ec80 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/EmbedFieldStrategy.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/EmbedFieldStrategy.java @@ -627,6 +627,10 @@ public class EmbedFieldStrategy return isFlushed(); } + public boolean isProvisional() { + return _owner.isProvisional(); + } + public BitSet getLoaded() { // consider everything loaded if (_full == null) { diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AttachManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AttachManager.java index 085d120b0..5b1eeb690 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AttachManager.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AttachManager.java @@ -94,7 +94,7 @@ class AttachManager { CallbackException excep = null; try { - return attach(pc, null, null, null); + return attach(pc, null, null, null, true); } catch (CallbackException ce) { excep = ce; return null; // won't be reached as the exceps will be rethrown @@ -122,7 +122,7 @@ class AttachManager { int i = 0; for (Iterator itr = instances.iterator(); itr.hasNext(); i++) { try { - attached[i] = attach(itr.next(), null, null, null); + attached[i] = attach(itr.next(), null, null, null, true); } catch (OpenJPAException ke) { // track exceptions and optimistic failed objects if (opt && !(ke instanceof OptimisticException)) @@ -212,9 +212,10 @@ class AttachManager { * @param into the instance we're attaching into * @param owner state manager for into * @param ownerMeta the field we traversed to find toAttach + * @param explicit whether to make new instances explicitly persistent */ Object attach(Object toAttach, PersistenceCapable into, - OpenJPAStateManager owner, ValueMetaData ownerMeta) { + OpenJPAStateManager owner, ValueMetaData ownerMeta, boolean explicit) { if (toAttach == null) return null; @@ -233,7 +234,7 @@ class AttachManager { getMetaDataRepositoryInstance().getMetaData(toAttach.getClass(), _broker.getClassLoader(), true); return getStrategy(toAttach).attach(this, toAttach, meta, into, - owner, ownerMeta); + owner, ownerMeta, explicit); } /** diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AttachStrategy.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AttachStrategy.java index 32b24fa82..26a9b0986 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AttachStrategy.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AttachStrategy.java @@ -55,10 +55,11 @@ abstract class AttachStrategy * @param into instance we're attaching into * @param owner state manager for into * @param ownerMeta field we traversed to find toAttach + * @param explicit whether to make new instances explicitly persistent */ public abstract Object attach(AttachManager manager, Object toAttach, ClassMetaData meta, PersistenceCapable into, - OpenJPAStateManager owner, ValueMetaData ownerMeta); + OpenJPAStateManager owner, ValueMetaData ownerMeta, boolean explicit); /** * Return the identity of the given detached instance. @@ -73,10 +74,12 @@ abstract class AttachStrategy int field); /** - * Return a PNew managed object for the given detached instance. + * Return a PNew/PNewProvisional managed object for the given detached + * instance. */ protected StateManagerImpl persist(AttachManager manager, - PersistenceCapable pc, ClassMetaData meta, Object appId) { + PersistenceCapable pc, ClassMetaData meta, Object appId, + boolean explicit) { PersistenceCapable newInstance; if (!manager.getCopyNew()) newInstance = pc; @@ -86,7 +89,7 @@ abstract class AttachStrategy newInstance = pc.pcNewInstance(null, appId, false); return (StateManagerImpl) manager.getBroker().persist - (newInstance, appId, manager.getBehavior()); + (newInstance, appId, explicit, manager.getBehavior()); } /** @@ -191,7 +194,7 @@ abstract class AttachStrategy manager.getDetachedObjectId(frmpc))) { intopc = null; } - frmpc = manager.attach(frmpc, intopc, sm, fmd); + frmpc = manager.attach(frmpc, intopc, sm, fmd, false); } if (frmpc != topc) sm.settingObjectField(into, i, topc, frmpc, set); @@ -320,7 +323,7 @@ abstract class AttachStrategy if (vmd.getCascadeAttach() == ValueMetaData.CASCADE_NONE) elem = getReference(manager, itr.next(), sm, vmd); else - elem = manager.attach(itr.next(), null, sm, vmd); + elem = manager.attach(itr.next(), null, sm, vmd, false); coll.add(elem); } return coll; @@ -432,13 +435,13 @@ abstract class AttachStrategy if (keymd.getCascadeAttach() == ValueMetaData.CASCADE_NONE) key = getReference(manager, key, sm, keymd); else - key = manager.attach(key, null, sm, keymd); + key = manager.attach(key, null, sm, keymd, false); val = entry.getValue(); if (valmd.isDeclaredTypePC()) { if (valmd.getCascadeAttach() == ValueMetaData.CASCADE_NONE) val = getReference(manager, val, sm, valmd); else - val = manager.attach(val, null, sm, valmd); + val = manager.attach(val, null, sm, valmd, false); } map.put(key, val); } @@ -449,7 +452,8 @@ abstract class AttachStrategy if (valmd.getCascadeAttach() == ValueMetaData.CASCADE_NONE) val = getReference(manager, entry.getValue(), sm, valmd); else - val = manager.attach(entry.getValue(), null, sm, valmd); + val = manager.attach(entry.getValue(), null, sm, valmd, + false); entry.setValue(val); } } @@ -479,7 +483,7 @@ abstract class AttachStrategy if (vmd.getCascadeAttach() == ValueMetaData.CASCADE_NONE) elem = getReference(manager, elem, sm, vmd); else - elem = manager.attach(elem, null, sm, vmd); + elem = manager.attach(elem, null, sm, vmd, false); } diff = diff || !equals(elem, Array.get(toa, i), pc); Array.set(newa, i, elem); diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java index f36f5f131..cbd9a438d 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java @@ -2152,7 +2152,25 @@ public class BrokerImpl // Object lifecycle //////////////////// + public void persist(Object obj, OpCallbacks call) { + persist(obj, null, true, call); + } + + public OpenJPAStateManager persist(Object obj, Object id, + OpCallbacks call) { + return persist(obj, id, true, call); + } + public void persistAll(Collection objs, OpCallbacks call) { + persistAll(objs, true, call); + } + + /** + * Persist the given objects. Indicate whether this was an explicit persist + * (PNEW) or a provisonal persist (PNEWPROVISIONAL). + */ + public void persistAll(Collection objs, boolean explicit, + OpCallbacks call) { if (objs.isEmpty()) return; @@ -2163,7 +2181,7 @@ public class BrokerImpl for (Iterator itr = objs.iterator(); itr.hasNext();) { try { - persist(itr.next(), call); + persist(itr.next(), explicit, call); } catch (UserException ue) { exceps = add(exceps, ue); } @@ -2212,11 +2230,20 @@ public class BrokerImpl throw err.setNestedThrowables(t).setFatal(fatal); } - public void persist(Object obj, OpCallbacks call) { - persist(obj, null, call); + /** + * Persist the given object. Indicate whether this was an explicit persist + * (PNEW) or a provisonal persist (PNEWPROVISIONAL) + */ + public void persist(Object obj, boolean explicit, OpCallbacks call) { + persist(obj, null, explicit, call); } - public OpenJPAStateManager persist(Object obj, Object id, + /** + * Persist the given object. Indicate whether this was an explicit persist + * (PNEW) or a provisonal persist (PNEWPROVISIONAL). + * See {@link Broker} for details on this method. + */ + public OpenJPAStateManager persist(Object obj, Object id, boolean explicit, OpCallbacks call) { if (obj == null) return null; @@ -2299,9 +2326,12 @@ public class BrokerImpl // create new sm sm = new StateManagerImpl(id, meta, this); - if ((_flags & FLAG_ACTIVE) != 0) - sm.initialize(pc, PCState.PNEW); - else + if ((_flags & FLAG_ACTIVE) != 0) { + if (explicit) + sm.initialize(pc, PCState.PNEW); + else + sm.initialize(pc, PCState.PNEWPROVISIONAL); + } else sm.initialize(pc, PCState.PNONTRANSNEW); if ((action & OpCallbacks.ACT_CASCADE) != 0) sm.cascadePersist(call); diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateAttachStrategy.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateAttachStrategy.java index 6f3a6a627..aaa6a481f 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateAttachStrategy.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateAttachStrategy.java @@ -70,7 +70,7 @@ class DetachedStateAttachStrategy public Object attach(AttachManager manager, Object toAttach, ClassMetaData meta, PersistenceCapable into, OpenJPAStateManager owner, - ValueMetaData ownerMeta) { + ValueMetaData ownerMeta, boolean explicit) { BrokerImpl broker = manager.getBroker(); PersistenceCapable pc = (PersistenceCapable) toAttach; @@ -94,7 +94,8 @@ class DetachedStateAttachStrategy sm = (StateManagerImpl) broker.embed(into, null, owner, ownerMeta); into = sm.getPersistenceCapable(); } else if (state == null) { - sm = persist(manager, pc, meta, ApplicationIds.create(pc, meta)); + sm = persist(manager, pc, meta, ApplicationIds.create(pc, meta), + explicit); into = sm.getPersistenceCapable(); } else if (!embedded && into == null) { Object id = getDetachedObjectId(manager, pc); @@ -117,7 +118,7 @@ class DetachedStateAttachStrategy // the transaction was rolled back; the danger is that // the instance was made persistent, detached, committed, // and then deleted, but this is an uncommon case - sm = persist(manager, pc, meta, id); + sm = persist(manager, pc, meta, id, explicit); into = sm.getPersistenceCapable(); // nullify the state, since the new instance won't have one @@ -144,7 +145,7 @@ class DetachedStateAttachStrategy // only attach fields in the FG of the detached instance; new // instances get all their fields attached if (fields == null || fields.get(i)) - attachField(manager, pc, sm, fmds[i], true); + attachField(manager, pc, sm, fmds[i], false); } } finally { diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java index 7f6f0141c..fc1c2acc8 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java @@ -87,7 +87,7 @@ public class DetachedStateManager public Object attach(AttachManager manager, Object toAttach, ClassMetaData meta, PersistenceCapable into, OpenJPAStateManager owner, - ValueMetaData ownerMeta) { + ValueMetaData ownerMeta, boolean explicit) { BrokerImpl broker = manager.getBroker(); StateManagerImpl sm = null; if (_embedded) { @@ -225,7 +225,8 @@ public class DetachedStateManager PersistenceCapable toPC = null; if (objval != null && fields[i].isEmbeddedPC()) toPC = (PersistenceCapable) objval; - objval = manager.attach(objval, toPC, sm, fields[i]); + objval = manager.attach(objval, toPC, sm, fields[i], + false); } if (_dirty.get(i)) sm.settingObjectField(pc, i, (!loaded.get(i)) ? null @@ -688,6 +689,10 @@ public class DetachedStateManager throw new UnsupportedOperationException(); } + public boolean isProvisional() { + throw new UnsupportedOperationException(); + } + public BitSet getLoaded() { return _loaded; } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedValueStateManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedValueStateManager.java index c2d03750c..74615d6f3 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedValueStateManager.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedValueStateManager.java @@ -101,6 +101,10 @@ public class DetachedValueStateManager return false; } + public boolean isProvisional() { + return false; + } + public BitSet getLoaded() { throw new UnsupportedOperationException(); } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ObjectIdStateManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ObjectIdStateManager.java index a12370a22..ab19a2fd2 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ObjectIdStateManager.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ObjectIdStateManager.java @@ -317,6 +317,10 @@ public class ObjectIdStateManager return false; } + public boolean isProvisional() { + return false; + } + public BitSet getLoaded() { throw new UnsupportedOperationException(); } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPAStateManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPAStateManager.java index 3a8adf8c7..6ab1dddf3 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPAStateManager.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPAStateManager.java @@ -120,6 +120,11 @@ public interface OpenJPAStateManager */ public boolean isFlushedDirty(); + /** + * Return whether this object is provisionally persistent. + */ + public boolean isProvisional(); + /** * Return a read-only mask of the indexes of all loaded fields. */ diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCState.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCState.java index 1bb48bcd9..5b9906696 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCState.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCState.java @@ -62,6 +62,11 @@ public class PCState */ public static final PCState PNEWDELETED = new PNewDeletedState(); + /** + * Persistent-New-Provisional + */ + public static final PCState PNEWPROVISIONAL = new PNewProvisionalState(); + /** * Persistent-Nontransactinoal */ @@ -228,6 +233,14 @@ public class PCState return this; } + /** + * Return the state to transition to after making no longer provisional. + * The context is not given because no actions should be taken. + */ + PCState nonprovisional() { + return this; + } + /** * Perform any actions necesssary and return the proper lifecycle state * on a call to {@link StoreContext#nontransactional} with the given @@ -402,6 +415,15 @@ public class PCState return false; } + /** + * Return whether this is a state that will become transient + * at the end of the next transaction. + * Returns false by default. + */ + boolean isProvisional() { + return false; + } + /** * Whether this state requires a version check when being flushed, * assuming the system is configured for version checks. @@ -430,6 +452,8 @@ public class PCState return PDELETED; if (this instanceof PNewDeletedState) return PNEWDELETED; + if (this instanceof PNewProvisionalState) + return PNEWPROVISIONAL; if (this instanceof PNonTransState) return PNONTRANS; if (this instanceof PNonTransDirtyState) diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PNewProvisionalState.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PNewProvisionalState.java new file mode 100755 index 000000000..4a9aded09 --- /dev/null +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PNewProvisionalState.java @@ -0,0 +1,48 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed 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.kernel; + +/** + * Lifecycle state. + * Represents an instance that was made persistent via reachability within the + * current transaction. + * + * @author Steve Kim + * @author: Abe White + */ +class PNewProvisionalState + extends PNewState { + + PCState persist(StateManagerImpl context) { + return PNEW; + } + + PCState nonprovisional() { + return PNEW; + } + + PCState commit(StateManagerImpl context) { + return TRANSIENT; + } + + PCState commitRetain(StateManagerImpl context) { + return TRANSIENT; + } + + boolean isProvisional() { + return true; + } +} diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java index a57670380..e99c97084 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java @@ -250,21 +250,22 @@ class SingleFieldManager case JavaTypes.PC_UNTYPED: if (!_broker.isDetachedNew() && _broker.isDetached(objval)) return; // allow but ignore - _broker.persist(objval, call); + _broker.persist(objval, false, call); break; case JavaTypes.ARRAY: - _broker.persistAll(Arrays.asList((Object[]) objval), call); + _broker.persistAll(Arrays.asList((Object[]) objval), false, + call); break; case JavaTypes.COLLECTION: - _broker.persistAll((Collection) objval, call); + _broker.persistAll((Collection) objval, false, call); break; case JavaTypes.MAP: if (fmd.getKey().getCascadePersist() == ValueMetaData.CASCADE_IMMEDIATE) - _broker.persistAll(((Map) objval).keySet(), call); + _broker.persistAll(((Map) objval).keySet(), false, call); if (fmd.getElement().getCascadePersist() == ValueMetaData.CASCADE_IMMEDIATE) - _broker.persistAll(((Map) objval).values(), call); + _broker.persistAll(((Map) objval).values(), false, call); break; } } @@ -467,7 +468,9 @@ class SingleFieldManager return false; // perform pers-by-reach and dependent refs - boolean ret = preFlush(fmd, call); + boolean ret = false; + if (!_sm.isProvisional()) + ret = preFlush(fmd, call); // manage inverses InverseManager manager = _broker.getInverseManager(); @@ -740,7 +743,7 @@ class SingleFieldManager Exceptions.toString(_sm.getManagedInstance()))). setFailedObject(obj); } else - sm = _broker.persist(obj, null, call); + sm = _broker.persist(obj, null, true, call); if (sm != null) { // if deleted and not managed inverse, die @@ -750,6 +753,7 @@ class SingleFieldManager Exceptions.toString(obj), vmd, Exceptions.toString(_sm.getManagedInstance()))). setFailedObject(obj); + ((StateManagerImpl) sm).nonprovisional(); ((StateManagerImpl) sm).setDereferencedDependent(false, true); } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java index 8336ad73a..e798a1b8d 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java @@ -993,6 +993,16 @@ public class StateManagerImpl setPCState(_state.transactional(this)); } + /** + * Delegates to the current state. + * + * @see PCState#nonprovisional + * @see Broker#nonprovisional + */ + void nonprovisional() { + setPCState(_state.nonprovisional()); + } + /** * Delegates to the current state. * @@ -1252,6 +1262,10 @@ public class StateManagerImpl return _state.isPendingTransactional(); } + public boolean isProvisional() { + return _state.isProvisional(); + } + public boolean isPersistent() { return _state.isPersistent(); } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionAttachStrategy.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionAttachStrategy.java index acb91eb0f..17de89ba7 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionAttachStrategy.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionAttachStrategy.java @@ -59,7 +59,7 @@ class VersionAttachStrategy public Object attach(AttachManager manager, Object toAttach, ClassMetaData meta, PersistenceCapable into, OpenJPAStateManager owner, - ValueMetaData ownerMeta) { + ValueMetaData ownerMeta, boolean explicit) { BrokerImpl broker = manager.getBroker(); PersistenceCapable pc = (PersistenceCapable) toAttach; @@ -82,7 +82,8 @@ class VersionAttachStrategy sm = (StateManagerImpl) broker.embed(into, null, owner, ownerMeta); into = sm.getPersistenceCapable(); } else if (isNew) { - sm = persist(manager, pc, meta, ApplicationIds.create(pc, meta)); + sm = persist(manager, pc, meta, ApplicationIds.create(pc, meta), + explicit); into = sm.getPersistenceCapable(); } else if (!embedded && into == null) { Object id = getDetachedObjectId(manager, toAttach); @@ -235,8 +236,8 @@ class VersionAttachStrategy PersistenceCapable intoPC = (into == null) ? null : into.getPersistenceCapable(); if (vmd.isEmbedded()) - return manager.attach(pc, intoPC, sm, vmd); - return manager.attach(pc, intoPC, null, null); + return manager.attach(pc, intoPC, sm, vmd, false); + return manager.attach(pc, intoPC, null, null, false); } /**