mirror of https://github.com/apache/openjpa.git
OPENJPA-2165 Added support for non-db-ordered list proxies that provide the ability to do non-indexed add or remove operations without loading the collection from the database. Testcases and documentation will follow in future commits.
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1306449 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
3d04bdd463
commit
3e1de6c8e3
|
@ -641,10 +641,15 @@ public class JDBCStoreManager implements StoreManager, JDBCStore {
|
||||||
&& mapping.customLoad(sm, this, null, jfetch))
|
&& mapping.customLoad(sm, this, null, jfetch))
|
||||||
removeLoadedFields(sm, fields);
|
removeLoadedFields(sm, fields);
|
||||||
|
|
||||||
|
|
||||||
//### select is kind of a big object, and in some cases we don't
|
//### select is kind of a big object, and in some cases we don't
|
||||||
//### use it... would it be worth it to have a small shell select
|
//### use it... would it be worth it to have a small shell select
|
||||||
//### object that only creates a real select when actually used?
|
//### object that only creates a real select when actually used?
|
||||||
|
//### Delayed proxy specific optimization: If the only fields that
|
||||||
|
//### need to be loaded are delayed proxies, building the select is
|
||||||
|
//### not necessary.
|
||||||
|
|
||||||
|
if (!isDelayedLoadOnly(sm, fields, mapping)) {
|
||||||
Select sel = _sql.newSelect();
|
Select sel = _sql.newSelect();
|
||||||
if (select(sel, mapping, Select.SUBS_EXACT, sm, fields, jfetch,
|
if (select(sel, mapping, Select.SUBS_EXACT, sm, fields, jfetch,
|
||||||
EagerFetchModes.EAGER_JOIN, true, false)) {
|
EagerFetchModes.EAGER_JOIN, true, false)) {
|
||||||
|
@ -661,11 +666,12 @@ public class JDBCStoreManager implements StoreManager, JDBCStore {
|
||||||
res.close();
|
res.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// now allow the fields to load themselves individually too
|
// now allow the fields to load themselves individually too
|
||||||
FieldMapping[] fms = mapping.getFieldMappings();
|
FieldMapping[] fms = mapping.getFieldMappings();
|
||||||
for (int i = 0; i < fms.length; i++)
|
for (int i = 0; i < fms.length; i++)
|
||||||
if (fields.get(i) && !sm.getLoaded().get(i)) {
|
if (fields.get(i) && (!sm.getLoaded().get(i) || sm.isDelayed(i))) {
|
||||||
if (_log.isTraceEnabled()) {
|
if (_log.isTraceEnabled()) {
|
||||||
_log.trace("load field: '"+ fms[i].getName() + "' for oid="+sm.getObjectId()
|
_log.trace("load field: '"+ fms[i].getName() + "' for oid="+sm.getObjectId()
|
||||||
+" "+mapping.getDescribedType());
|
+" "+mapping.getDescribedType());
|
||||||
|
@ -681,6 +687,30 @@ public class JDBCStoreManager implements StoreManager, JDBCStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isDelayedLoadOnly(OpenJPAStateManager sm, BitSet fields, ClassMapping mapping) {
|
||||||
|
if (!sm.getContext().getConfiguration().getProxyManagerInstance().getDelayCollectionLoading()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean allDelayed = false;
|
||||||
|
if (!fields.isEmpty()) {
|
||||||
|
FieldMapping[] fms = mapping.getFieldMappings();
|
||||||
|
int fCount = 0;
|
||||||
|
int dfCount = 0;
|
||||||
|
for (int i = fields.nextSetBit(0); i < fms.length; i++) {
|
||||||
|
if (fields.get(i)) {
|
||||||
|
fCount++;
|
||||||
|
if (!(fms[i].isDelayCapable() && (!sm.getLoaded().get(i) || sm.isDelayed(i)))) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
dfCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allDelayed = (fCount == dfCount);
|
||||||
|
}
|
||||||
|
return allDelayed;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a list formed by removing all loaded fields from the given one.
|
* Return a list formed by removing all loaded fields from the given one.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1359,4 +1359,9 @@ public class FieldMapping
|
||||||
public boolean hasMapsIdCols() {
|
public boolean hasMapsIdCols() {
|
||||||
return _hasMapsIdCols;
|
return _hasMapsIdCols;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDelayCapable() {
|
||||||
|
return (getOrderColumn() == null && super.isDelayCapable());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1252,6 +1252,21 @@ public class EmbedFieldStrategy
|
||||||
public Object replaceObjectField(PersistenceCapable pc, int field) {
|
public Object replaceObjectField(PersistenceCapable pc, int field) {
|
||||||
throw new InternalException();
|
throw new InternalException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDelayed(int field) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDelayed(int field, boolean delay) {
|
||||||
|
throw new InternalException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadDelayedField(int field) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.openjpa.enhance.FieldManager;
|
||||||
import org.apache.openjpa.enhance.PersistenceCapable;
|
import org.apache.openjpa.enhance.PersistenceCapable;
|
||||||
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
|
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
|
||||||
import org.apache.openjpa.jdbc.kernel.JDBCStore;
|
import org.apache.openjpa.jdbc.kernel.JDBCStore;
|
||||||
|
@ -42,13 +43,13 @@ import org.apache.openjpa.jdbc.sql.SelectExecutor;
|
||||||
import org.apache.openjpa.jdbc.sql.Union;
|
import org.apache.openjpa.jdbc.sql.Union;
|
||||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||||
import org.apache.openjpa.kernel.StateManagerImpl;
|
import org.apache.openjpa.kernel.StateManagerImpl;
|
||||||
import org.apache.openjpa.lib.util.Localizer;
|
|
||||||
import org.apache.openjpa.meta.ClassMetaData;
|
import org.apache.openjpa.meta.ClassMetaData;
|
||||||
import org.apache.openjpa.meta.JavaTypes;
|
import org.apache.openjpa.meta.JavaTypes;
|
||||||
import org.apache.openjpa.util.ChangeTracker;
|
import org.apache.openjpa.util.ChangeTracker;
|
||||||
import org.apache.openjpa.util.Id;
|
import org.apache.openjpa.util.Id;
|
||||||
import org.apache.openjpa.util.OpenJPAId;
|
import org.apache.openjpa.util.OpenJPAId;
|
||||||
import org.apache.openjpa.util.Proxy;
|
import org.apache.openjpa.util.Proxy;
|
||||||
|
import org.apache.openjpa.util.DelayedProxy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for strategies that are stored as a collection, even if
|
* Base class for strategies that are stored as a collection, even if
|
||||||
|
@ -491,8 +492,20 @@ public abstract class StoreCollectionFieldStrategy
|
||||||
public void load(final OpenJPAStateManager sm, final JDBCStore store,
|
public void load(final OpenJPAStateManager sm, final JDBCStore store,
|
||||||
final JDBCFetchConfiguration fetch)
|
final JDBCFetchConfiguration fetch)
|
||||||
throws SQLException {
|
throws SQLException {
|
||||||
|
|
||||||
|
Object coll = null;
|
||||||
|
boolean delayed = sm.isDelayed(field.getIndex());
|
||||||
|
if (!delayed && field.isDelayCapable()) {
|
||||||
|
coll = sm.newProxy(field.getIndex());
|
||||||
|
if (coll instanceof DelayedProxy) {
|
||||||
|
sm.storeObject(field.getIndex(), coll);
|
||||||
|
sm.setDelayed(field.getIndex(), true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (field.isLRS()) {
|
if (field.isLRS()) {
|
||||||
Proxy coll = newLRSProxy();
|
Proxy pcoll = newLRSProxy();
|
||||||
|
|
||||||
// if this is ordered we need to know the next seq to use in case
|
// if this is ordered we need to know the next seq to use in case
|
||||||
// objects are added to the collection
|
// objects are added to the collection
|
||||||
|
@ -513,13 +526,13 @@ public abstract class StoreCollectionFieldStrategy
|
||||||
Result res = sel.execute(store, fetch);
|
Result res = sel.execute(store, fetch);
|
||||||
try {
|
try {
|
||||||
res.next();
|
res.next();
|
||||||
coll.getChangeTracker().setNextSequence
|
pcoll.getChangeTracker().setNextSequence
|
||||||
(res.getInt(field) + 1);
|
(res.getInt(field) + 1);
|
||||||
} finally {
|
} finally {
|
||||||
res.close();
|
res.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sm.storeObjectField(field.getIndex(), coll);
|
sm.storeObjectField(field.getIndex(), pcoll);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,15 +550,28 @@ public abstract class StoreCollectionFieldStrategy
|
||||||
});
|
});
|
||||||
|
|
||||||
// create proxy
|
// create proxy
|
||||||
Object coll;
|
|
||||||
ChangeTracker ct = null;
|
ChangeTracker ct = null;
|
||||||
|
if (delayed) {
|
||||||
|
if (sm.isDetached() || sm.getOwner() == null) {
|
||||||
|
sm.getPersistenceCapable().pcProvideField(field.getIndex());
|
||||||
|
coll =
|
||||||
|
((FieldManager)sm.getPersistenceCapable().pcGetStateManager()).fetchObjectField(field.getIndex());
|
||||||
|
} else {
|
||||||
|
coll = sm.fetchObjectField(field.getIndex());
|
||||||
|
}
|
||||||
|
if (coll instanceof Proxy)
|
||||||
|
ct = ((Proxy) coll).getChangeTracker();
|
||||||
|
} else {
|
||||||
if (field.getTypeCode() == JavaTypes.ARRAY)
|
if (field.getTypeCode() == JavaTypes.ARRAY)
|
||||||
coll = new ArrayList();
|
coll = new ArrayList();
|
||||||
else {
|
else {
|
||||||
|
if (coll == null) {
|
||||||
coll = sm.newProxy(field.getIndex());
|
coll = sm.newProxy(field.getIndex());
|
||||||
|
}
|
||||||
if (coll instanceof Proxy)
|
if (coll instanceof Proxy)
|
||||||
ct = ((Proxy) coll).getChangeTracker();
|
ct = ((Proxy) coll).getChangeTracker();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// load values
|
// load values
|
||||||
Result res = union.execute(store, fetch);
|
Result res = union.execute(store, fetch);
|
||||||
|
@ -564,13 +590,15 @@ public abstract class StoreCollectionFieldStrategy
|
||||||
res.close();
|
res.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// set into sm
|
// if not a delayed collection, set into sm
|
||||||
|
if (!delayed) {
|
||||||
if (field.getTypeCode() == JavaTypes.ARRAY)
|
if (field.getTypeCode() == JavaTypes.ARRAY)
|
||||||
sm.storeObject(field.getIndex(), JavaTypes.toArray
|
sm.storeObject(field.getIndex(), JavaTypes.toArray
|
||||||
((Collection) coll, field.getElement().getType()));
|
((Collection) coll, field.getElement().getType()));
|
||||||
else
|
else
|
||||||
sm.storeObject(field.getIndex(), coll);
|
sm.storeObject(field.getIndex(), coll);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select data for loading, starting in field table.
|
* Select data for loading, starting in field table.
|
||||||
|
|
|
@ -690,6 +690,18 @@ public class TestUpdateManagerFlushException extends /* Abstract */TestCase {
|
||||||
public String fetchStringField(int fieldIndex) {
|
public String fetchStringField(int fieldIndex) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDelayed(int field) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDelayed(int field, boolean delay) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadDelayedField(int field) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -995,4 +995,19 @@ public class DetachedStateManager
|
||||||
if (_lock != null)
|
if (_lock != null)
|
||||||
_lock.unlock();
|
_lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDelayed(int field) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDelayed(int field, boolean delay) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadDelayedField(int field) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -595,5 +595,20 @@ public class DetachedValueStateManager
|
||||||
public Object replaceObjectField(PersistenceCapable pc, int idx) {
|
public Object replaceObjectField(PersistenceCapable pc, int idx) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDelayed(int field) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDelayed(int field, boolean delay) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadDelayedField(int field) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -739,4 +739,19 @@ public class ObjectIdStateManager
|
||||||
Reflection.set(_oid, Reflection.findSetter(_oid.getClass(),
|
Reflection.set(_oid, Reflection.findSetter(_oid.getClass(),
|
||||||
fmd.getName(), fmd.getDeclaredType(), true), val);
|
fmd.getName(), fmd.getDeclaredType(), true), val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDelayed(int field) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDelayed(int field, boolean delay) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadDelayedField(int field) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -498,5 +498,37 @@ public interface OpenJPAStateManager
|
||||||
* @since 0.3.1
|
* @since 0.3.1
|
||||||
*/
|
*/
|
||||||
public void setRemote (int field, Object value);
|
public void setRemote (int field, Object value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some field types (collection proxies) support delayed loading. Delayed loading
|
||||||
|
* is a step beyond lazy loading. Delayed load allows an instance of a field to be
|
||||||
|
* returned without actually loading it.
|
||||||
|
*
|
||||||
|
* @param field
|
||||||
|
* @return true if the field is setup for delayed access
|
||||||
|
*/
|
||||||
|
public boolean isDelayed(int field);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some field types (collection proxies) support delayed loading. Delayed loading
|
||||||
|
* is a step beyond lazy loading. Delayed load allows an instance of a field to be
|
||||||
|
* returned without actually loading it.
|
||||||
|
*
|
||||||
|
* @param field
|
||||||
|
*/
|
||||||
|
public void setDelayed(int field, boolean delay);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a field was marked delayed in a previous load operation this method can be
|
||||||
|
* used to load the field.
|
||||||
|
* @param field
|
||||||
|
*/
|
||||||
|
public void loadDelayedField(int field);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch an object field by index.
|
||||||
|
* @param field
|
||||||
|
*/
|
||||||
|
public Object fetchObjectField(int field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,7 @@ public class StateManagerImpl
|
||||||
protected BitSet _loaded = null;
|
protected BitSet _loaded = null;
|
||||||
private BitSet _dirty = null;
|
private BitSet _dirty = null;
|
||||||
private BitSet _flush = null;
|
private BitSet _flush = null;
|
||||||
|
private BitSet _delayed = null;
|
||||||
private int _flags = 0;
|
private int _flags = 0;
|
||||||
|
|
||||||
// id is the state manager identity; oid is the persistent identity. oid
|
// id is the state manager identity; oid is the persistent identity. oid
|
||||||
|
@ -1600,6 +1601,58 @@ public class StateManagerImpl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isDelayed(int field) {
|
||||||
|
if (_delayed == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return _delayed.get(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDelayed(int field, boolean delay) {
|
||||||
|
if (_delayed == null) {
|
||||||
|
_delayed = new BitSet();
|
||||||
|
}
|
||||||
|
if (delay) {
|
||||||
|
_delayed.set(field);
|
||||||
|
} else {
|
||||||
|
_delayed.clear(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a delayed access field.
|
||||||
|
* @param field
|
||||||
|
*/
|
||||||
|
public void loadDelayedField(int field) {
|
||||||
|
if (!isDelayed(field)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
beforeRead(field);
|
||||||
|
} catch (RuntimeException re) {
|
||||||
|
throw translate(re);
|
||||||
|
}
|
||||||
|
lock();
|
||||||
|
try {
|
||||||
|
boolean active = _broker.isActive();
|
||||||
|
int lockLevel = calculateLockLevel(active, false, null);
|
||||||
|
BitSet fields = new BitSet();
|
||||||
|
fields.set(field);
|
||||||
|
if (!_broker.getStoreManager().load(this, fields, _broker.getFetchConfiguration(), lockLevel, null)) {
|
||||||
|
throw new ObjectNotFoundException(_loc.get("del-instance", _meta.getDescribedType(), _oid)).
|
||||||
|
setFailedObject(getManagedInstance());
|
||||||
|
}
|
||||||
|
// Cleared the delayed bit
|
||||||
|
_delayed.clear(field);
|
||||||
|
obtainLocks(active, false, lockLevel, null, null);
|
||||||
|
} catch (RuntimeException re) {
|
||||||
|
throw translate(re);
|
||||||
|
} finally {
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the given field before access.
|
* Load the given field before access.
|
||||||
*/
|
*/
|
||||||
|
@ -3420,4 +3473,8 @@ public class StateManagerImpl
|
||||||
public void setPc(PersistenceCapable pc) {
|
public void setPc(PersistenceCapable pc) {
|
||||||
_pc = pc;
|
_pc = pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setBroker(BrokerImpl ctx) {
|
||||||
|
_broker = ctx;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,7 @@ import org.apache.openjpa.util.Exceptions;
|
||||||
import org.apache.openjpa.util.InternalException;
|
import org.apache.openjpa.util.InternalException;
|
||||||
import org.apache.openjpa.util.MetaDataException;
|
import org.apache.openjpa.util.MetaDataException;
|
||||||
import org.apache.openjpa.util.OpenJPAException;
|
import org.apache.openjpa.util.OpenJPAException;
|
||||||
|
import org.apache.openjpa.util.ProxyManager;
|
||||||
import org.apache.openjpa.util.UnsupportedException;
|
import org.apache.openjpa.util.UnsupportedException;
|
||||||
import org.apache.openjpa.util.ImplHelper;
|
import org.apache.openjpa.util.ImplHelper;
|
||||||
import org.apache.openjpa.util.UserException;
|
import org.apache.openjpa.util.UserException;
|
||||||
|
@ -222,6 +223,7 @@ public class FieldMetaData
|
||||||
|
|
||||||
private boolean _persistentCollection = false;
|
private boolean _persistentCollection = false;
|
||||||
|
|
||||||
|
private Boolean _delayCapable = null;
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
|
@ -2405,4 +2407,27 @@ public class FieldMetaData
|
||||||
return _relationType;
|
return _relationType;
|
||||||
}
|
}
|
||||||
private class Unknown{};
|
private class Unknown{};
|
||||||
|
|
||||||
|
public boolean isDelayCapable() {
|
||||||
|
if (_delayCapable != null) {
|
||||||
|
return _delayCapable.booleanValue();
|
||||||
|
}
|
||||||
|
if (getTypeCode() != JavaTypes.COLLECTION || isLRS()) {
|
||||||
|
_delayCapable = Boolean.FALSE;
|
||||||
|
return _delayCapable;
|
||||||
|
} else {
|
||||||
|
// Verify the proxy manager is configured to handle delay loading
|
||||||
|
ProxyManager pm = getRepository().getConfiguration().getProxyManagerInstance();
|
||||||
|
if (pm != null) {
|
||||||
|
_delayCapable = pm.getDelayCollectionLoading();
|
||||||
|
} else {
|
||||||
|
_delayCapable = Boolean.FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _delayCapable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDelayCapable(Boolean delayCapable) {
|
||||||
|
_delayCapable = delayCapable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,9 @@ public class CollectionChangeTrackerImpl
|
||||||
extends AbstractChangeTracker
|
extends AbstractChangeTracker
|
||||||
implements CollectionChangeTracker {
|
implements CollectionChangeTracker {
|
||||||
|
|
||||||
private final Collection _coll;
|
protected final Collection _coll;
|
||||||
private final boolean _dups;
|
protected final boolean _dups;
|
||||||
private final boolean _order;
|
protected final boolean _order;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
|
|
|
@ -0,0 +1,430 @@
|
||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import java.io.ObjectStreamException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
|
||||||
|
import org.apache.openjpa.kernel.AutoDetach;
|
||||||
|
import org.apache.openjpa.kernel.Broker;
|
||||||
|
import org.apache.openjpa.kernel.BrokerFactory;
|
||||||
|
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||||
|
|
||||||
|
public class DelayedArrayListProxy extends ArrayList
|
||||||
|
implements ProxyCollection, DelayedProxy
|
||||||
|
{
|
||||||
|
private transient OpenJPAStateManager sm;
|
||||||
|
private transient OpenJPAStateManager _ownerSm;
|
||||||
|
private transient int field;
|
||||||
|
private transient CollectionChangeTracker changeTracker;
|
||||||
|
private transient Class<?> elementType;
|
||||||
|
private transient boolean _directAccess = false;
|
||||||
|
private transient BrokerFactory _brokerFactory = null;
|
||||||
|
private transient Broker _broker = null;
|
||||||
|
private transient OpenJPAStateManager _delayedSm;
|
||||||
|
private transient int _delayedField;
|
||||||
|
private transient boolean dirtyCollection = true;
|
||||||
|
|
||||||
|
public DelayedArrayListProxy()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DelayedArrayListProxy(Collection paramCollection)
|
||||||
|
{
|
||||||
|
super(paramCollection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DelayedArrayListProxy(int paramInt)
|
||||||
|
{
|
||||||
|
super(paramInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOwner(OpenJPAStateManager paramOpenJPAStateManager, int paramInt)
|
||||||
|
{
|
||||||
|
// If clearing the owner of this proxy, store away what is necessary for
|
||||||
|
// delayed loading
|
||||||
|
if (paramOpenJPAStateManager == null && paramInt == -1 && sm != null) {
|
||||||
|
_delayedSm = sm;
|
||||||
|
_delayedField = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sm = paramOpenJPAStateManager;
|
||||||
|
if (sm != null && sm.getPersistenceCapable() != null) {
|
||||||
|
_ownerSm = (OpenJPAStateManager) sm.getPersistenceCapable().pcGetStateManager();
|
||||||
|
}
|
||||||
|
this.field = paramInt;
|
||||||
|
if (sm != null && sm.getContext() != null) {
|
||||||
|
_brokerFactory = sm.getContext().getBroker().getBrokerFactory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDelayedField() {
|
||||||
|
if (field == -1) {
|
||||||
|
return _delayedField;
|
||||||
|
}
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpenJPAStateManager getDelayedOwner() {
|
||||||
|
if (sm == null) {
|
||||||
|
return _delayedSm;
|
||||||
|
}
|
||||||
|
return sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpenJPAStateManager getOwner()
|
||||||
|
{
|
||||||
|
return sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOwnerField()
|
||||||
|
{
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object clone()
|
||||||
|
{
|
||||||
|
if (isDirectAccess()) {
|
||||||
|
return super.clone();
|
||||||
|
}
|
||||||
|
if (isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
Proxy localProxy = (Proxy)super.clone();
|
||||||
|
localProxy.setOwner(null, 0);
|
||||||
|
return localProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChangeTracker getChangeTracker()
|
||||||
|
{
|
||||||
|
return this.changeTracker;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setChangeTracker(CollectionChangeTracker ct) {
|
||||||
|
changeTracker = ct;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object copy(Object paramObject)
|
||||||
|
{
|
||||||
|
return new ArrayList((Collection)paramObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getElementType()
|
||||||
|
{
|
||||||
|
return this.elementType;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setElementType(Class<?> elemType) {
|
||||||
|
elementType = elemType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProxyCollection newInstance(Class paramClass, Comparator paramComparator, boolean paramBoolean1,
|
||||||
|
boolean paramBoolean2)
|
||||||
|
{
|
||||||
|
DelayedArrayListProxy proxy = new DelayedArrayListProxy();
|
||||||
|
proxy.elementType = paramClass;
|
||||||
|
proxy.changeTracker = new DelayedCollectionChangeTrackerImpl(proxy, true, true, paramBoolean2);
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean add(Object paramObject)
|
||||||
|
{
|
||||||
|
if (_directAccess) {
|
||||||
|
return super.add(paramObject);
|
||||||
|
}
|
||||||
|
ProxyCollections.beforeAdd(this, paramObject);
|
||||||
|
boolean bool = super.add(paramObject);
|
||||||
|
return ProxyCollections.afterAdd(this, paramObject, bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(int paramInt, Object paramObject)
|
||||||
|
{
|
||||||
|
if (!_directAccess) {
|
||||||
|
if (isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ProxyCollections.beforeAdd(this, paramInt, paramObject);
|
||||||
|
super.add(paramInt, paramObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear()
|
||||||
|
{
|
||||||
|
if (!_directAccess) {
|
||||||
|
if (isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
ProxyCollections.beforeClear(this);
|
||||||
|
}
|
||||||
|
super.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addAll(int paramInt, Collection paramCollection)
|
||||||
|
{
|
||||||
|
if (isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
return ProxyCollections.addAll(this, paramInt, paramCollection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addAll(Collection paramCollection)
|
||||||
|
{
|
||||||
|
if (_directAccess) {
|
||||||
|
return super.addAll(paramCollection);
|
||||||
|
}
|
||||||
|
return ProxyCollections.addAll(this, paramCollection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean remove(Object paramObject)
|
||||||
|
{
|
||||||
|
if (_directAccess) {
|
||||||
|
return super.remove(paramObject);
|
||||||
|
}
|
||||||
|
ProxyCollections.beforeRemove(this, paramObject);
|
||||||
|
setDirectAccess(true);
|
||||||
|
boolean bool = super.remove(paramObject);
|
||||||
|
setDirectAccess(false);
|
||||||
|
return ProxyCollections.afterRemove(this, paramObject, bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object remove(int paramInt)
|
||||||
|
{
|
||||||
|
if (_directAccess) {
|
||||||
|
return super.remove(paramInt);
|
||||||
|
}
|
||||||
|
if (isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
ProxyCollections.beforeRemove(this, paramInt);
|
||||||
|
Object localObject = super.remove(paramInt);
|
||||||
|
return ProxyCollections.afterRemove(this, paramInt, localObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object set(int paramInt, Object paramObject)
|
||||||
|
{
|
||||||
|
if (_directAccess) {
|
||||||
|
return super.set(paramInt, paramObject);
|
||||||
|
}
|
||||||
|
if (isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
ProxyCollections.beforeSet(this, paramInt, paramObject);
|
||||||
|
Object localObject = super.set(paramInt, paramObject);
|
||||||
|
return ProxyCollections.afterSet(this, paramInt, paramObject, localObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator iterator()
|
||||||
|
{
|
||||||
|
if (_directAccess) {
|
||||||
|
return super.iterator();
|
||||||
|
}
|
||||||
|
if (isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
Iterator localIterator = super.iterator();
|
||||||
|
return ProxyCollections.afterIterator(this, localIterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListIterator listIterator(int paramInt)
|
||||||
|
{
|
||||||
|
if (_directAccess) {
|
||||||
|
return super.listIterator(paramInt);
|
||||||
|
}
|
||||||
|
if (isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
ListIterator localListIterator = super.listIterator(paramInt);
|
||||||
|
return ProxyCollections.afterListIterator(this, paramInt, localListIterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListIterator listIterator()
|
||||||
|
{
|
||||||
|
if (_directAccess) {
|
||||||
|
return super.listIterator();
|
||||||
|
}
|
||||||
|
if (isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
ListIterator localListIterator = super.listIterator();
|
||||||
|
return ProxyCollections.afterListIterator(this, localListIterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean removeAll(Collection paramCollection)
|
||||||
|
{
|
||||||
|
if (_directAccess) {
|
||||||
|
return super.removeAll(paramCollection);
|
||||||
|
}
|
||||||
|
if (isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
return ProxyCollections.removeAll(this, paramCollection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean retainAll(Collection paramCollection)
|
||||||
|
{
|
||||||
|
if (_directAccess) {
|
||||||
|
return super.retainAll(paramCollection);
|
||||||
|
}
|
||||||
|
if (isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
return ProxyCollections.retainAll(this, paramCollection);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object writeReplace()
|
||||||
|
throws ObjectStreamException
|
||||||
|
{
|
||||||
|
if (isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
return Proxies.writeReplace(this, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDelayLoad() {
|
||||||
|
return ProxyCollections.isDelayed(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(int location) {
|
||||||
|
if (!_directAccess && isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
return super.get(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int indexOf(Object object) {
|
||||||
|
if (!_directAccess && isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
return super.indexOf(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int lastIndexOf(Object object) {
|
||||||
|
if (!_directAccess && isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
return super.lastIndexOf(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List subList(int start, int end) {
|
||||||
|
if (!_directAccess && isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
return super.subList(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object object) {
|
||||||
|
if (!_directAccess && isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
return super.contains(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(Collection collection) {
|
||||||
|
if (!_directAccess && isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
return super.containsAll(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
if (!_directAccess && isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
return super.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
if (!_directAccess && isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
return super.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] toArray() {
|
||||||
|
if (!_directAccess && isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
return super.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] toArray(Object[] array) {
|
||||||
|
if (!_directAccess && isDelayLoad()) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
return super.toArray(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDirectAccess() {
|
||||||
|
return _directAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDirectAccess(boolean direct) {
|
||||||
|
_directAccess = direct;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BrokerFactory getBrokerFactory() {
|
||||||
|
return _brokerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void load() {
|
||||||
|
ProxyCollections.loadCollection(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Broker getBroker() {
|
||||||
|
if (_broker == null || _broker.isClosed()) {
|
||||||
|
if (_brokerFactory != null) {
|
||||||
|
_broker = _brokerFactory.newBroker();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _broker;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void closeBroker() {
|
||||||
|
if (_broker != null && !_broker.isClosed()) {
|
||||||
|
_broker.setAutoDetach(AutoDetach.DETACH_CLOSE);
|
||||||
|
_broker.close();
|
||||||
|
_broker = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OpenJPAStateManager getOwnerStateManager() {
|
||||||
|
return _ownerSm;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection change tracker used by delay loaded collections.
|
||||||
|
*
|
||||||
|
* @nojavadoc
|
||||||
|
*/
|
||||||
|
public class DelayedCollectionChangeTrackerImpl
|
||||||
|
extends CollectionChangeTrackerImpl {
|
||||||
|
|
||||||
|
public DelayedCollectionChangeTrackerImpl(Collection coll, boolean dups,
|
||||||
|
boolean order,boolean autoOff) {
|
||||||
|
super(coll, dups, order, autoOff);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void add(Object elem) {
|
||||||
|
if (rem == null || !rem.remove(elem)) {
|
||||||
|
if (add == null) {
|
||||||
|
if (_dups || _order)
|
||||||
|
add = new ArrayList();
|
||||||
|
else
|
||||||
|
add = newSet();
|
||||||
|
}
|
||||||
|
add.add(elem);
|
||||||
|
} else {
|
||||||
|
if (change == null)
|
||||||
|
change = newSet();
|
||||||
|
change.add(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void remove(Object elem) {
|
||||||
|
if (add == null || !add.remove(elem)) {
|
||||||
|
if (rem == null)
|
||||||
|
rem = newSet();
|
||||||
|
rem.add(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void change(Object elem) {
|
||||||
|
throw new InternalException();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import org.apache.openjpa.kernel.Broker;
|
||||||
|
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||||
|
|
||||||
|
public interface DelayedProxy {
|
||||||
|
|
||||||
|
void load();
|
||||||
|
|
||||||
|
boolean isDirectAccess();
|
||||||
|
|
||||||
|
void setDirectAccess(boolean direct);
|
||||||
|
|
||||||
|
Broker getBroker();
|
||||||
|
|
||||||
|
void closeBroker();
|
||||||
|
|
||||||
|
OpenJPAStateManager getOwnerStateManager();
|
||||||
|
|
||||||
|
OpenJPAStateManager getDelayedOwner();
|
||||||
|
|
||||||
|
int getDelayedField();
|
||||||
|
}
|
|
@ -18,12 +18,18 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.openjpa.util;
|
package org.apache.openjpa.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
|
|
||||||
|
import org.apache.openjpa.kernel.Broker;
|
||||||
|
import org.apache.openjpa.kernel.BrokerImpl;
|
||||||
|
import org.apache.openjpa.kernel.DetachedValueStateManager;
|
||||||
|
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||||
|
import org.apache.openjpa.kernel.StateManagerImpl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility methods used by collection proxies.
|
* Utility methods used by collection proxies.
|
||||||
*
|
*
|
||||||
|
@ -54,8 +60,11 @@ public class ProxyCollections
|
||||||
*/
|
*/
|
||||||
public static void beforeAdd(ProxyCollection coll, Object value) {
|
public static void beforeAdd(ProxyCollection coll, Object value) {
|
||||||
assertAllowedType(value, coll.getElementType());
|
assertAllowedType(value, coll.getElementType());
|
||||||
|
// Must only dirty the collection outside of a delayed load
|
||||||
|
if (!isDirectAccess(coll)) {
|
||||||
dirty(coll, false);
|
dirty(coll, false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call after invoking {@link Collection#add(Object)} on super.
|
* Call after invoking {@link Collection#add(Object)} on super.
|
||||||
|
@ -65,8 +74,11 @@ public class ProxyCollections
|
||||||
*/
|
*/
|
||||||
public static boolean afterAdd(ProxyCollection coll, Object value,
|
public static boolean afterAdd(ProxyCollection coll, Object value,
|
||||||
boolean added) {
|
boolean added) {
|
||||||
if (added && coll.getChangeTracker() != null)
|
if (!isDirectAccess(coll) && added && coll.getChangeTracker() != null) {
|
||||||
|
setDirectAccess(coll,true);
|
||||||
((CollectionChangeTracker) coll.getChangeTracker()).added(value);
|
((CollectionChangeTracker) coll.getChangeTracker()).added(value);
|
||||||
|
setDirectAccess(coll,false);
|
||||||
|
}
|
||||||
return added;
|
return added;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +151,7 @@ public class ProxyCollections
|
||||||
*/
|
*/
|
||||||
public static boolean addAll(ProxyCollection coll, Collection values) {
|
public static boolean addAll(ProxyCollection coll, Collection values) {
|
||||||
boolean added = false;
|
boolean added = false;
|
||||||
for (Iterator itr = values.iterator(); itr.hasNext();)
|
for (Iterator<?> itr = values.iterator(); itr.hasNext();)
|
||||||
added |= coll.add(itr.next());
|
added |= coll.add(itr.next());
|
||||||
return added;
|
return added;
|
||||||
}
|
}
|
||||||
|
@ -149,7 +161,7 @@ public class ProxyCollections
|
||||||
*/
|
*/
|
||||||
public static void beforeClear(ProxyCollection coll) {
|
public static void beforeClear(ProxyCollection coll) {
|
||||||
dirty(coll, true);
|
dirty(coll, true);
|
||||||
for (Iterator itr = coll.iterator(); itr.hasNext();)
|
for (Iterator<?> itr = coll.iterator(); itr.hasNext();)
|
||||||
removed(coll, itr.next(), false);
|
removed(coll, itr.next(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,8 +316,11 @@ public class ProxyCollections
|
||||||
* Call before invoking {@link Collection#remove} on super.
|
* Call before invoking {@link Collection#remove} on super.
|
||||||
*/
|
*/
|
||||||
public static void beforeRemove(ProxyCollection coll, Object o) {
|
public static void beforeRemove(ProxyCollection coll, Object o) {
|
||||||
|
// Must only dirty the collection outside of a delayed load
|
||||||
|
if (!isDirectAccess(coll)) {
|
||||||
dirty(coll, false);
|
dirty(coll, false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call after invoking {@link Collection#remove} on super.
|
* Call after invoking {@link Collection#remove} on super.
|
||||||
|
@ -315,14 +330,40 @@ public class ProxyCollections
|
||||||
*/
|
*/
|
||||||
public static boolean afterRemove(ProxyCollection coll, Object o,
|
public static boolean afterRemove(ProxyCollection coll, Object o,
|
||||||
boolean removed){
|
boolean removed){
|
||||||
|
boolean isDelayed = isDelayed(coll);
|
||||||
|
boolean direct = isDirectAccess(coll);
|
||||||
|
if (!isDelayed) {
|
||||||
if (!removed)
|
if (!removed)
|
||||||
return false;
|
return false;
|
||||||
if (coll.getChangeTracker() != null)
|
}
|
||||||
|
if (!direct && coll.getChangeTracker() != null) {
|
||||||
|
// switch on direct access to prevent the removed op from
|
||||||
|
// inadvertently loading the collection
|
||||||
|
setDirectAccess(coll, true);
|
||||||
((CollectionChangeTracker) coll.getChangeTracker()).removed(o);
|
((CollectionChangeTracker) coll.getChangeTracker()).removed(o);
|
||||||
|
setDirectAccess(coll, false);
|
||||||
|
}
|
||||||
|
if (!isDelayed) {
|
||||||
removed(coll, o, false);
|
removed(coll, o, false);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isDirectAccess(ProxyCollection coll) {
|
||||||
|
if (coll instanceof DelayedProxy) {
|
||||||
|
DelayedProxy dpxy = (DelayedProxy)coll;
|
||||||
|
return dpxy.isDirectAccess();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setDirectAccess(ProxyCollection coll, boolean direct) {
|
||||||
|
if (coll instanceof DelayedProxy) {
|
||||||
|
DelayedProxy dpxy = (DelayedProxy)coll;
|
||||||
|
dpxy.setDirectAccess(direct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call before invoking {@link Vector#removeElement} on super.
|
* Call before invoking {@link Vector#removeElement} on super.
|
||||||
*/
|
*/
|
||||||
|
@ -400,9 +441,9 @@ public class ProxyCollections
|
||||||
/**
|
/**
|
||||||
* Override for {@link Collection#removeAll}.
|
* Override for {@link Collection#removeAll}.
|
||||||
*/
|
*/
|
||||||
public static boolean removeAll(ProxyCollection coll, Collection vals) {
|
public static boolean removeAll(ProxyCollection coll, Collection<?> vals) {
|
||||||
boolean removed = false;
|
boolean removed = false;
|
||||||
for (Iterator itr = vals.iterator(); itr.hasNext();)
|
for (Iterator<?> itr = vals.iterator(); itr.hasNext();)
|
||||||
removed |= coll.remove(itr.next());
|
removed |= coll.remove(itr.next());
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
@ -410,9 +451,9 @@ public class ProxyCollections
|
||||||
/**
|
/**
|
||||||
* Override for {@link Collection#retainAll}.
|
* Override for {@link Collection#retainAll}.
|
||||||
*/
|
*/
|
||||||
public static boolean retainAll(ProxyCollection coll, Collection vals) {
|
public static boolean retainAll(ProxyCollection coll, Collection<?> vals) {
|
||||||
int size = coll.size();
|
int size = coll.size();
|
||||||
for (Iterator itr = coll.iterator(); itr.hasNext();)
|
for (Iterator<?> itr = coll.iterator(); itr.hasNext();)
|
||||||
if (!vals.contains(itr.next()))
|
if (!vals.contains(itr.next()))
|
||||||
itr.remove();
|
itr.remove();
|
||||||
return coll.size() < size;
|
return coll.size() < size;
|
||||||
|
@ -469,4 +510,110 @@ public class ProxyCollections
|
||||||
public static interface ProxyListIterator
|
public static interface ProxyListIterator
|
||||||
extends ProxyIterator, ListIterator {
|
extends ProxyIterator, ListIterator {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void loadCollection(ProxyCollection proxy) {
|
||||||
|
loadCollection(proxy, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadCollection(ProxyCollection proxy, boolean detaching) {
|
||||||
|
if (!isDelayed(proxy)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DelayedProxy dProxy = (DelayedProxy)proxy;
|
||||||
|
if (dProxy.isDirectAccess()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean state[] = new boolean[2];
|
||||||
|
try {
|
||||||
|
dProxy.setDirectAccess(true);
|
||||||
|
state = checkState(proxy);
|
||||||
|
boolean tracking = false;
|
||||||
|
ChangeTracker ct = proxy.getChangeTracker();
|
||||||
|
Collection<?> added = null;
|
||||||
|
Collection<?> removed = null;
|
||||||
|
if (ct != null && ct.isTracking() ) {
|
||||||
|
if (!ct.getAdded().isEmpty()) {
|
||||||
|
added = new ArrayList(ct.getAdded());
|
||||||
|
}
|
||||||
|
if (!ct.getRemoved().isEmpty()) {
|
||||||
|
removed = new ArrayList(ct.getRemoved());
|
||||||
|
}
|
||||||
|
tracking = true;
|
||||||
|
ct.stopTracking();
|
||||||
|
}
|
||||||
|
if (proxy.size() > 0) {
|
||||||
|
proxy.clear();
|
||||||
|
}
|
||||||
|
dProxy.getDelayedOwner().loadDelayedField(dProxy.getDelayedField());
|
||||||
|
if (!detaching && tracking && !ct.isTracking()) {
|
||||||
|
ct.startTracking();
|
||||||
|
}
|
||||||
|
// add new elements
|
||||||
|
if (added != null && added.size() > 0) {
|
||||||
|
dProxy.setDirectAccess(false);
|
||||||
|
proxy.addAll(added);
|
||||||
|
added.clear();
|
||||||
|
}
|
||||||
|
// purge removed elements
|
||||||
|
if (removed != null && removed.size() > 0) {
|
||||||
|
dProxy.setDirectAccess(false);
|
||||||
|
proxy.removeAll(removed);
|
||||||
|
removed.clear();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
dProxy.setDirectAccess(false);
|
||||||
|
if (state[0]) {
|
||||||
|
dProxy.closeBroker();
|
||||||
|
}
|
||||||
|
if (state[1]) {
|
||||||
|
clearStateManager(proxy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDelayed(ProxyCollection proxy) {
|
||||||
|
if (proxy instanceof DelayedProxy) {
|
||||||
|
DelayedProxy dProxy = (DelayedProxy)proxy;
|
||||||
|
OpenJPAStateManager sm = dProxy.getDelayedOwner();
|
||||||
|
return (sm != null &&
|
||||||
|
sm.isDelayed(dProxy.getDelayedField()));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean[] checkState(ProxyCollection proxy) {
|
||||||
|
boolean[] state = new boolean[2];
|
||||||
|
DelayedProxy dProxy = (DelayedProxy)proxy;
|
||||||
|
|
||||||
|
OpenJPAStateManager sm = dProxy.getDelayedOwner();
|
||||||
|
if (sm != null) {
|
||||||
|
// If the broker assigned to this proxy is null, closed or no longer
|
||||||
|
// manages the pc, produce a new one
|
||||||
|
Broker broker = sm.getContext().getBroker();
|
||||||
|
if (broker == null || broker.isClosed()
|
||||||
|
|| (!broker.isClosed() && !broker.isPersistent(sm.getPersistenceCapable()))) {
|
||||||
|
state[0] = true;
|
||||||
|
broker = dProxy.getBroker();
|
||||||
|
((StateManagerImpl)sm).setBroker((BrokerImpl)broker);
|
||||||
|
}
|
||||||
|
if (sm.getPersistenceCapable().pcGetStateManager() == null) {
|
||||||
|
state[1] = true;
|
||||||
|
if (dProxy.getOwnerStateManager() != null) {
|
||||||
|
sm.getPersistenceCapable().pcReplaceStateManager(dProxy.getOwnerStateManager());
|
||||||
|
((StateManagerImpl)dProxy.getOwnerStateManager()).setBroker((BrokerImpl)broker);
|
||||||
|
} else {
|
||||||
|
sm.getPersistenceCapable().pcReplaceStateManager(
|
||||||
|
new DetachedValueStateManager(sm.getPersistenceCapable(), sm.getContext()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void clearStateManager(ProxyCollection proxy) {
|
||||||
|
OpenJPAStateManager sm = proxy.getOwner();
|
||||||
|
if (sm != null) {
|
||||||
|
sm.getPersistenceCapable().pcReplaceStateManager(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,4 +113,15 @@ public interface ProxyManager {
|
||||||
* @since 0.2.5
|
* @since 0.2.5
|
||||||
*/
|
*/
|
||||||
public Proxy newCustomProxy (Object obj, boolean autoOff);
|
public Proxy newCustomProxy (Object obj, boolean autoOff);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this proxy manager is enabled for delayed collection
|
||||||
|
* loading. Delayed collection loading provides the ability to do simple,
|
||||||
|
* non-indexed add or remove operations on a lazy collection without
|
||||||
|
* loading the collection. The collection is loaded when necessary, such
|
||||||
|
* as iteration, indexed operations, isEmpty, or size.
|
||||||
|
*
|
||||||
|
* @since 2.2.1
|
||||||
|
*/
|
||||||
|
public boolean getDelayCollectionLoading();
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,7 @@ public class ProxyManagerImpl
|
||||||
private final Map _proxies = new NullSafeConcurrentHashMap();
|
private final Map _proxies = new NullSafeConcurrentHashMap();
|
||||||
private boolean _trackChanges = true;
|
private boolean _trackChanges = true;
|
||||||
private boolean _assertType = false;
|
private boolean _assertType = false;
|
||||||
|
private boolean _delayedCollectionLoading = false;
|
||||||
|
|
||||||
public ProxyManagerImpl() {
|
public ProxyManagerImpl() {
|
||||||
_unproxyable.add(TimeZone.class.getName());
|
_unproxyable.add(TimeZone.class.getName());
|
||||||
|
@ -139,6 +140,25 @@ public class ProxyManagerImpl
|
||||||
_assertType = assertType;
|
_assertType = assertType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether loading of collections should be delayed until an operation
|
||||||
|
* is performed that requires them to be loaded. This property only
|
||||||
|
* applies to proxies that implement java.util.Collection (ie. not arrays
|
||||||
|
* or maps). Defaults to false.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean getDelayCollectionLoading() {
|
||||||
|
return _delayedCollectionLoading;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether loading of collections should be delayed until an operation
|
||||||
|
* is performed that requires them to be loaded. Defaults to false.
|
||||||
|
*/
|
||||||
|
public void setDelayCollectionLoading(boolean delay) {
|
||||||
|
_delayedCollectionLoading = delay;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a mutable view of class names we know cannot be proxied
|
* Return a mutable view of class names we know cannot be proxied
|
||||||
* correctly by this manager.
|
* correctly by this manager.
|
||||||
|
@ -477,6 +497,9 @@ public class ProxyManagerImpl
|
||||||
*/
|
*/
|
||||||
protected Class loadBuildTimeProxy(Class type, ClassLoader loader) {
|
protected Class loadBuildTimeProxy(Class type, ClassLoader loader) {
|
||||||
try {
|
try {
|
||||||
|
if (_delayedCollectionLoading && type.equals(java.util.ArrayList.class)) {
|
||||||
|
return org.apache.openjpa.util.DelayedArrayListProxy.class;
|
||||||
|
}
|
||||||
return Class.forName(getProxyClassName(type, false), true, loader);
|
return Class.forName(getProxyClassName(type, false), true, loader);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
return null;
|
return null;
|
||||||
|
|
Loading…
Reference in New Issue