mirror of https://github.com/apache/openjpa.git
OPENJPA-134 : When we're performing eager fetches and we can detect that we're
already fetching the owning side of a bidi relation, cut off eager selecting and loading when we come across the back-ptr to the owner again. git-svn-id: https://svn.apache.org/repos/asf/incubator/openjpa/trunk@527565 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
0806fd5b44
commit
b4a3a77840
|
@ -233,14 +233,6 @@ public class DelegatingJDBCFetchConfiguration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public JDBCFetchConfiguration traverseJDBC(FieldMetaData fm) {
|
|
||||||
try {
|
|
||||||
return getJDBCDelegate().traverseJDBC(fm);
|
|
||||||
} catch (RuntimeException re) {
|
|
||||||
throw translate(re);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getIsolation() {
|
public int getIsolation() {
|
||||||
try {
|
try {
|
||||||
return getJDBCDelegate().getIsolation();
|
return getJDBCDelegate().getIsolation();
|
||||||
|
@ -257,4 +249,12 @@ public class DelegatingJDBCFetchConfiguration
|
||||||
throw translate(re);
|
throw translate(re);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JDBCFetchConfiguration traverseJDBC(FieldMetaData fm) {
|
||||||
|
try {
|
||||||
|
return getJDBCDelegate().traverseJDBC(fm);
|
||||||
|
} catch (RuntimeException re) {
|
||||||
|
throw translate(re);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,11 +166,6 @@ public interface JDBCFetchConfiguration
|
||||||
*/
|
*/
|
||||||
public JDBCFetchConfiguration clearJoins();
|
public JDBCFetchConfiguration clearJoins();
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience method to cast traversal to store-specific type.
|
|
||||||
*/
|
|
||||||
public JDBCFetchConfiguration traverseJDBC(FieldMetaData fm);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>The isolation level for queries issued to the database. This overrides
|
* <p>The isolation level for queries issued to the database. This overrides
|
||||||
* the persistence-unit-wide <code>openjpa.jdbc.TransactionIsolation</code>
|
* the persistence-unit-wide <code>openjpa.jdbc.TransactionIsolation</code>
|
||||||
|
@ -204,4 +199,9 @@ public interface JDBCFetchConfiguration
|
||||||
* @since 0.9.7
|
* @since 0.9.7
|
||||||
*/
|
*/
|
||||||
public JDBCFetchConfiguration setIsolation(int level);
|
public JDBCFetchConfiguration setIsolation(int level);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method to cast traversal to store-specific type.
|
||||||
|
*/
|
||||||
|
public JDBCFetchConfiguration traverseJDBC(FieldMetaData fm);
|
||||||
}
|
}
|
||||||
|
|
|
@ -303,25 +303,6 @@ public class JDBCFetchConfigurationImpl
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JDBCFetchConfiguration traverseJDBC(FieldMetaData fm) {
|
|
||||||
return (JDBCFetchConfiguration) traverse(fm);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Access JDBC configuration information. May return null if not a
|
|
||||||
* JDBC back-end (possible to get a JDBCFetchConfiguration on non-JDBC
|
|
||||||
* back end in remote client).
|
|
||||||
*/
|
|
||||||
private JDBCConfiguration getJDBCConfiguration() {
|
|
||||||
StoreContext ctx = getContext();
|
|
||||||
if (ctx == null)
|
|
||||||
return null;
|
|
||||||
OpenJPAConfiguration conf = ctx.getConfiguration();
|
|
||||||
if (!(conf instanceof JDBCConfiguration))
|
|
||||||
return null;
|
|
||||||
return (JDBCConfiguration) conf;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getIsolation() {
|
public int getIsolation() {
|
||||||
return _state.isolationLevel;
|
return _state.isolationLevel;
|
||||||
}
|
}
|
||||||
|
@ -342,4 +323,23 @@ public class JDBCFetchConfigurationImpl
|
||||||
_state.isolationLevel = level;
|
_state.isolationLevel = level;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JDBCFetchConfiguration traverseJDBC(FieldMetaData fm) {
|
||||||
|
return (JDBCFetchConfiguration) traverse(fm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access JDBC configuration information. May return null if not a
|
||||||
|
* JDBC back-end (possible to get a JDBCFetchConfiguration on non-JDBC
|
||||||
|
* back end in remote client).
|
||||||
|
*/
|
||||||
|
private JDBCConfiguration getJDBCConfiguration() {
|
||||||
|
StoreContext ctx = getContext();
|
||||||
|
if (ctx == null)
|
||||||
|
return null;
|
||||||
|
OpenJPAConfiguration conf = ctx.getConfiguration();
|
||||||
|
if (!(conf instanceof JDBCConfiguration))
|
||||||
|
return null;
|
||||||
|
return (JDBCConfiguration) conf;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -756,8 +756,8 @@ public class JDBCStoreManager
|
||||||
public Object load(ClassMapping mapping, JDBCFetchConfiguration fetch,
|
public Object load(ClassMapping mapping, JDBCFetchConfiguration fetch,
|
||||||
BitSet exclude, Result result) throws SQLException {
|
BitSet exclude, Result result) throws SQLException {
|
||||||
if (!mapping.isMapped())
|
if (!mapping.isMapped())
|
||||||
throw new InvalidStateException(_loc
|
throw new InvalidStateException(_loc.get("virtual-mapping",
|
||||||
.get("virtual-mapping", mapping));
|
mapping));
|
||||||
|
|
||||||
// get the object id for the row; base class selects pk columns
|
// get the object id for the row; base class selects pk columns
|
||||||
ClassMapping base = mapping;
|
ClassMapping base = mapping;
|
||||||
|
@ -972,7 +972,7 @@ public class JDBCStoreManager
|
||||||
if (sm != null && sm.getPCState() != PCState.TRANSIENT
|
if (sm != null && sm.getPCState() != PCState.TRANSIENT
|
||||||
&& sm.getLoaded().get(fm.getIndex()))
|
&& sm.getLoaded().get(fm.getIndex()))
|
||||||
return false;
|
return false;
|
||||||
return fetch.requiresFetch(fm);
|
return fetch.requiresFetch(fm) == FetchConfiguration.FETCH_LOAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1127,7 +1127,7 @@ public class JDBCStoreManager
|
||||||
fms = subMappings[i].getDefinedFieldMappings();
|
fms = subMappings[i].getDefinedFieldMappings();
|
||||||
for (int j = 0; j < fms.length; j++) {
|
for (int j = 0; j < fms.length; j++) {
|
||||||
// make sure in one of configured fetch groups
|
// make sure in one of configured fetch groups
|
||||||
if (!fetch.requiresFetch(fms[j])
|
if (fetch.requiresFetch(fms[j]) != FetchConfiguration.FETCH_LOAD
|
||||||
&& ((!fms[j].isInDefaultFetchGroup()
|
&& ((!fms[j].isInDefaultFetchGroup()
|
||||||
&& fms[j].isDefaultFetchGroupExplicit())
|
&& fms[j].isDefaultFetchGroupExplicit())
|
||||||
|| fms[j].supportsSelect(sel, sel.TYPE_TWO_PART, sm, this,
|
|| fms[j].supportsSelect(sel, sel.TYPE_TWO_PART, sm, this,
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.openjpa.jdbc.sql.Result;
|
||||||
import org.apache.openjpa.jdbc.sql.SQLBuffer;
|
import org.apache.openjpa.jdbc.sql.SQLBuffer;
|
||||||
import org.apache.openjpa.jdbc.sql.Select;
|
import org.apache.openjpa.jdbc.sql.Select;
|
||||||
import org.apache.openjpa.jdbc.sql.SelectExecutor;
|
import org.apache.openjpa.jdbc.sql.SelectExecutor;
|
||||||
|
import org.apache.openjpa.kernel.FetchConfiguration;
|
||||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||||
import org.apache.openjpa.kernel.StoreContext;
|
import org.apache.openjpa.kernel.StoreContext;
|
||||||
import org.apache.openjpa.lib.util.Closeable;
|
import org.apache.openjpa.lib.util.Closeable;
|
||||||
|
@ -90,7 +91,7 @@ public class PagingResultObjectProvider
|
||||||
FieldMapping[] fms = mapping.getDefinedFieldMappings();
|
FieldMapping[] fms = mapping.getDefinedFieldMappings();
|
||||||
BitSet paged = null;
|
BitSet paged = null;
|
||||||
for (int i = 0; i < fms.length; i++) {
|
for (int i = 0; i < fms.length; i++) {
|
||||||
if (!fetch.requiresFetch(fms[i]))
|
if (fetch.requiresFetch(fms[i]) != FetchConfiguration.FETCH_LOAD)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (fms[i].supportsSelect(sel, sel.EAGER_PARALLEL, null, store,
|
if (fms[i].supportsSelect(sel, sel.EAGER_PARALLEL, null, store,
|
||||||
|
|
|
@ -777,7 +777,7 @@ public class BrokerImpl
|
||||||
StateManagerImpl sm = getStateManagerImplById(oid,
|
StateManagerImpl sm = getStateManagerImplById(oid,
|
||||||
(flags & OID_ALLOW_NEW) != 0 || (_flags & FLAG_FLUSHED) != 0);
|
(flags & OID_ALLOW_NEW) != 0 || (_flags & FLAG_FLUSHED) != 0);
|
||||||
if (sm != null) {
|
if (sm != null) {
|
||||||
if (!requiresLoad(sm, true, edata, flags))
|
if (!requiresLoad(sm, true, fetch, edata, flags))
|
||||||
return call.processReturn(oid, sm);
|
return call.processReturn(oid, sm);
|
||||||
|
|
||||||
if (!sm.isLoading()) {
|
if (!sm.isLoading()) {
|
||||||
|
@ -827,7 +827,7 @@ public class BrokerImpl
|
||||||
|
|
||||||
// initialize a new state manager for the datastore instance
|
// initialize a new state manager for the datastore instance
|
||||||
sm = newStateManagerImpl(oid, (flags & OID_COPY) != 0);
|
sm = newStateManagerImpl(oid, (flags & OID_COPY) != 0);
|
||||||
boolean load = requiresLoad(sm, false, edata, flags);
|
boolean load = requiresLoad(sm, false, fetch, edata, flags);
|
||||||
sm = initialize(sm, load, fetch, edata);
|
sm = initialize(sm, load, fetch, edata);
|
||||||
if (sm == null) {
|
if (sm == null) {
|
||||||
if ((flags & OID_NOVALIDATE) != 0)
|
if ((flags & OID_NOVALIDATE) != 0)
|
||||||
|
@ -939,7 +939,7 @@ public class BrokerImpl
|
||||||
sm = newStateManagerImpl(oid, (flags & OID_COPY) != 0);
|
sm = newStateManagerImpl(oid, (flags & OID_COPY) != 0);
|
||||||
|
|
||||||
_loading.put(obj, sm);
|
_loading.put(obj, sm);
|
||||||
if (requiresLoad(sm, initialized, edata, flags)) {
|
if (requiresLoad(sm, initialized, fetch, edata, flags)) {
|
||||||
transState = transState || useTransactionalState(fetch);
|
transState = transState || useTransactionalState(fetch);
|
||||||
if (initialized && !sm.isTransactional() && transState)
|
if (initialized && !sm.isTransactional() && transState)
|
||||||
sm.transactional();
|
sm.transactional();
|
||||||
|
@ -977,7 +977,7 @@ public class BrokerImpl
|
||||||
for (Iterator itr = oids.iterator(); itr.hasNext(); idx++) {
|
for (Iterator itr = oids.iterator(); itr.hasNext(); idx++) {
|
||||||
oid = itr.next();
|
oid = itr.next();
|
||||||
sm = (StateManagerImpl) _loading.get(oid);
|
sm = (StateManagerImpl) _loading.get(oid);
|
||||||
if (sm != null && requiresLoad(sm, true, edata, flags)) {
|
if (sm != null && requiresLoad(sm, true, fetch, edata, flags)) {
|
||||||
try {
|
try {
|
||||||
sm.load(fetch, StateManagerImpl.LOAD_FGS,
|
sm.load(fetch, StateManagerImpl.LOAD_FGS,
|
||||||
exclude, edata, false);
|
exclude, edata, false);
|
||||||
|
@ -1011,7 +1011,9 @@ public class BrokerImpl
|
||||||
* to the user.
|
* to the user.
|
||||||
*/
|
*/
|
||||||
private boolean requiresLoad(OpenJPAStateManager sm, boolean initialized,
|
private boolean requiresLoad(OpenJPAStateManager sm, boolean initialized,
|
||||||
Object edata, int flags) {
|
FetchConfiguration fetch, Object edata, int flags) {
|
||||||
|
if (!fetch.requiresLoad())
|
||||||
|
return false;
|
||||||
if ((flags & OID_NOVALIDATE) == 0)
|
if ((flags & OID_NOVALIDATE) == 0)
|
||||||
return true;
|
return true;
|
||||||
if (edata != null) // take advantage of existing result
|
if (edata != null) // take advantage of existing result
|
||||||
|
|
|
@ -436,7 +436,7 @@ public class DelegatingFetchConfiguration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean requiresFetch(FieldMetaData fmd) {
|
public int requiresFetch(FieldMetaData fmd) {
|
||||||
try {
|
try {
|
||||||
return _fetch.requiresFetch(fmd);
|
return _fetch.requiresFetch(fmd);
|
||||||
} catch (RuntimeException re) {
|
} catch (RuntimeException re) {
|
||||||
|
@ -444,6 +444,14 @@ public class DelegatingFetchConfiguration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean requiresLoad() {
|
||||||
|
try {
|
||||||
|
return _fetch.requiresLoad();
|
||||||
|
} catch (RuntimeException re) {
|
||||||
|
throw translate(re);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public FetchConfiguration traverse(FieldMetaData fmd) {
|
public FetchConfiguration traverse(FieldMetaData fmd) {
|
||||||
try {
|
try {
|
||||||
return _fetch.traverse(fmd);
|
return _fetch.traverse(fmd);
|
||||||
|
|
|
@ -231,9 +231,11 @@ public class DetachManager
|
||||||
StateManagerImpl sm, BitSet idxs) {
|
StateManagerImpl sm, BitSet idxs) {
|
||||||
FetchConfiguration fetch = broker.getFetchConfiguration();
|
FetchConfiguration fetch = broker.getFetchConfiguration();
|
||||||
FieldMetaData[] fmds = sm.getMetaData().getFields();
|
FieldMetaData[] fmds = sm.getMetaData().getFields();
|
||||||
for (int i = 0; i < fmds.length; i++)
|
for (int i = 0; i < fmds.length; i++) {
|
||||||
if (fmds[i].isPrimaryKey() || fetch.requiresFetch(fmds[i]))
|
if (fmds[i].isPrimaryKey() || fetch.requiresFetch(fmds[i])
|
||||||
|
!= FetchConfiguration.FETCH_NONE)
|
||||||
idxs.set(i);
|
idxs.set(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -39,6 +39,32 @@ public interface FetchConfiguration
|
||||||
*/
|
*/
|
||||||
public static final int DEFAULT = -99;
|
public static final int DEFAULT = -99;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant indicating that a field does not require fetching.
|
||||||
|
*
|
||||||
|
* @see #requiresFetch
|
||||||
|
*/
|
||||||
|
public static final int FETCH_NONE = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant indicating that a field requires a fetch and load of fetched
|
||||||
|
* data.
|
||||||
|
*
|
||||||
|
* @see #requiresFetch
|
||||||
|
*/
|
||||||
|
public static final int FETCH_LOAD = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant indicating that a reference to the field's value must be
|
||||||
|
* fetched, but loading data is not necessary. Used when we know the
|
||||||
|
* data will be loaded anyway, such as when traversing the back-ptr of
|
||||||
|
* a bidirectional relation where the other side is also being fetched.
|
||||||
|
*
|
||||||
|
* @see #requiresFetch
|
||||||
|
*/
|
||||||
|
public static final int FETCH_REF = 2;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the context assiciated with this configuration;
|
* Return the context assiciated with this configuration;
|
||||||
* may be null if it has not been set or this object has been serialized.
|
* may be null if it has not been set or this object has been serialized.
|
||||||
|
@ -309,11 +335,19 @@ public interface FetchConfiguration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Affirms if the given field requires to be fetched in the context
|
* Affirms if the given field requires to be fetched in the context
|
||||||
* of current fetch operation.
|
* of current fetch operation. Returns one of {@link #FETCH_NONE},
|
||||||
|
* {@link #FETCH_LOAD}, {@link FETCH_REF}.
|
||||||
*
|
*
|
||||||
* @since 0.4.1
|
* @since 0.4.1
|
||||||
*/
|
*/
|
||||||
public boolean requiresFetch(FieldMetaData fm);
|
public int requiresFetch(FieldMetaData fm);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return false if we know that the object being fetched with this
|
||||||
|
* configuration does not require a load, because this configuration came
|
||||||
|
* from a traversal of a {@link #FETCH_REF} field.
|
||||||
|
*/
|
||||||
|
public boolean requiresLoad();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Traverse the given field to generate (possibly) a new configuration
|
* Traverse the given field to generate (possibly) a new configuration
|
||||||
|
|
|
@ -39,6 +39,7 @@ import org.apache.openjpa.lib.util.Localizer;
|
||||||
import org.apache.openjpa.meta.ClassMetaData;
|
import org.apache.openjpa.meta.ClassMetaData;
|
||||||
import org.apache.openjpa.meta.FetchGroup;
|
import org.apache.openjpa.meta.FetchGroup;
|
||||||
import org.apache.openjpa.meta.FieldMetaData;
|
import org.apache.openjpa.meta.FieldMetaData;
|
||||||
|
import org.apache.openjpa.meta.JavaTypes;
|
||||||
import org.apache.openjpa.util.ImplHelper;
|
import org.apache.openjpa.util.ImplHelper;
|
||||||
import org.apache.openjpa.util.InternalException;
|
import org.apache.openjpa.util.InternalException;
|
||||||
import org.apache.openjpa.util.NoTransactionException;
|
import org.apache.openjpa.util.NoTransactionException;
|
||||||
|
@ -84,6 +85,8 @@ public class FetchConfigurationImpl
|
||||||
private FetchConfigurationImpl _parent;
|
private FetchConfigurationImpl _parent;
|
||||||
private String _fromField;
|
private String _fromField;
|
||||||
private Class _fromType;
|
private Class _fromType;
|
||||||
|
private String _directRelationOwner;
|
||||||
|
private boolean _load = true;
|
||||||
private int _availableRecursion;
|
private int _availableRecursion;
|
||||||
private int _availableDepth;
|
private int _availableDepth;
|
||||||
|
|
||||||
|
@ -126,6 +129,8 @@ public class FetchConfigurationImpl
|
||||||
clone._parent = _parent;
|
clone._parent = _parent;
|
||||||
clone._fromField = _fromField;
|
clone._fromField = _fromField;
|
||||||
clone._fromType = _fromType;
|
clone._fromType = _fromType;
|
||||||
|
clone._directRelationOwner = _directRelationOwner;
|
||||||
|
clone._load = _load;
|
||||||
clone._availableRecursion = _availableRecursion;
|
clone._availableRecursion = _availableRecursion;
|
||||||
clone._availableDepth = _availableDepth;
|
clone._availableDepth = _availableDepth;
|
||||||
clone.copy(this);
|
clone.copy(this);
|
||||||
|
@ -498,23 +503,32 @@ public class FetchConfigurationImpl
|
||||||
// Traversal
|
// Traversal
|
||||||
/////////////
|
/////////////
|
||||||
|
|
||||||
public boolean requiresFetch(FieldMetaData fm) {
|
public int requiresFetch(FieldMetaData fm) {
|
||||||
if (!includes(fm))
|
if (!includes(fm))
|
||||||
return false;
|
return FETCH_NONE;
|
||||||
|
|
||||||
Class type = getRelationType(fm);
|
Class type = getRelationType(fm);
|
||||||
if (type == null)
|
if (type == null)
|
||||||
return true;
|
return FETCH_LOAD;
|
||||||
if (_availableDepth == 0)
|
if (_availableDepth == 0)
|
||||||
return false;
|
return FETCH_NONE;
|
||||||
|
|
||||||
// we can skip calculating recursion depth if this is a top-level conf:
|
// we can skip calculating recursion depth if this is a top-level conf:
|
||||||
// the field is in our fetch groups, so can't possibly not select
|
// the field is in our fetch groups, so can't possibly not select
|
||||||
if (_parent == null)
|
if (_parent == null)
|
||||||
return true;
|
return FETCH_LOAD;
|
||||||
|
|
||||||
int rdepth = getAvailableRecursionDepth(fm, type, false);
|
int rdepth = getAvailableRecursionDepth(fm, type, false);
|
||||||
return rdepth == FetchGroup.DEPTH_INFINITE || rdepth > 0;
|
if (rdepth != FetchGroup.DEPTH_INFINITE && rdepth <= 0)
|
||||||
|
return FETCH_NONE;
|
||||||
|
|
||||||
|
if (StringUtils.equals(_directRelationOwner, fm.getFullName()))
|
||||||
|
return FETCH_REF;
|
||||||
|
return FETCH_LOAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean requiresLoad() {
|
||||||
|
return _load;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FetchConfiguration traverse(FieldMetaData fm) {
|
public FetchConfiguration traverse(FieldMetaData fm) {
|
||||||
|
@ -528,6 +542,15 @@ public class FetchConfigurationImpl
|
||||||
clone._fromField = fm.getFullName(false);
|
clone._fromField = fm.getFullName(false);
|
||||||
clone._fromType = type;
|
clone._fromType = type;
|
||||||
clone._availableRecursion = getAvailableRecursionDepth(fm, type, true);
|
clone._availableRecursion = getAvailableRecursionDepth(fm, type, true);
|
||||||
|
if (StringUtils.equals(_directRelationOwner, fm.getFullName()))
|
||||||
|
clone._load = false;
|
||||||
|
else
|
||||||
|
clone._load = _load;
|
||||||
|
|
||||||
|
FieldMetaData owner = fm.getMappedByMetaData();
|
||||||
|
if (owner != null && owner.getTypeCode() == JavaTypes.PC)
|
||||||
|
clone._directRelationOwner = owner.getFullName();
|
||||||
|
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -138,7 +138,8 @@ public class PCDataImpl
|
||||||
// fields in configured fetch groups
|
// fields in configured fetch groups
|
||||||
if (!isLoaded(i))
|
if (!isLoaded(i))
|
||||||
loadIntermediate(sm, fmds[i]);
|
loadIntermediate(sm, fmds[i]);
|
||||||
else if (!sm.getLoaded().get(i) && fetch.requiresFetch(fmds[i]))
|
else if (!sm.getLoaded().get(i) && fetch.requiresFetch(fmds[i])
|
||||||
|
!= FetchConfiguration.FETCH_NONE)
|
||||||
loadField(sm, fmds[i], fetch, context);
|
loadField(sm, fmds[i], fetch, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -407,7 +407,8 @@ public class StateManagerImpl
|
||||||
load = !fmds[i].isTransient();
|
load = !fmds[i].isTransient();
|
||||||
break;
|
break;
|
||||||
case LOAD_FGS:
|
case LOAD_FGS:
|
||||||
load = fetch == null || fetch.requiresFetch(fmds[i]);
|
load = fetch == null || fetch.requiresFetch(fmds[i])
|
||||||
|
!= FetchConfiguration.FETCH_NONE;
|
||||||
break;
|
break;
|
||||||
default: // LOAD_ALL
|
default: // LOAD_ALL
|
||||||
load = true;
|
load = true;
|
||||||
|
|
|
@ -131,7 +131,8 @@ class VersionAttachStrategy
|
||||||
attachField(manager, toAttach, sm, fmds[i], true);
|
attachField(manager, toAttach, sm, fmds[i], true);
|
||||||
break;
|
break;
|
||||||
case DETACH_FGS:
|
case DETACH_FGS:
|
||||||
if (fetch.requiresFetch(fmds[i]))
|
if (fetch.requiresFetch(fmds[i])
|
||||||
|
!= FetchConfiguration.FETCH_NONE)
|
||||||
attachField(manager, toAttach, sm, fmds[i], true);
|
attachField(manager, toAttach, sm, fmds[i], true);
|
||||||
break;
|
break;
|
||||||
case DETACH_LOADED:
|
case DETACH_LOADED:
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* 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.persistence.relations;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToOne;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class BidiChild {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ManyToOne(fetch=FetchType.LAZY)
|
||||||
|
private BidiParent oneToManyParent;
|
||||||
|
|
||||||
|
@OneToOne(fetch=FetchType.LAZY)
|
||||||
|
private BidiParent oneToOneParent;
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BidiParent getOneToOneParent() {
|
||||||
|
return oneToOneParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOneToOneParent(BidiParent parent) {
|
||||||
|
oneToOneParent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BidiParent getOneToManyParent() {
|
||||||
|
return oneToManyParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOneToManyParent(BidiParent parent) {
|
||||||
|
oneToManyParent = parent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* 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.persistence.relations;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.OneToOne;
|
||||||
|
import javax.persistence.OrderBy;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class BidiParent {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy="oneToManyParent")
|
||||||
|
@OrderBy("name ASC")
|
||||||
|
private List<BidiChild> oneToManyChildren = new ArrayList<BidiChild>();
|
||||||
|
|
||||||
|
@OneToOne(fetch=FetchType.LAZY, mappedBy="oneToOneParent")
|
||||||
|
private BidiChild oneToOneChild;
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BidiChild getOneToOneChild() {
|
||||||
|
return oneToOneChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOneToOneChild(BidiChild child) {
|
||||||
|
oneToOneChild = child;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BidiChild> getOneToManyChildren() {
|
||||||
|
return oneToManyChildren;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
/*
|
||||||
|
* 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.persistence.relations;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
|
||||||
|
import junit.textui.TestRunner;
|
||||||
|
import org.apache.openjpa.persistence.OpenJPAEntityManager;
|
||||||
|
import org.apache.openjpa.persistence.OpenJPAQuery;
|
||||||
|
import org.apache.openjpa.persistence.test.SQLListenerTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that when both sides of a mapped-by relation are eager fetched,
|
||||||
|
* traversing from the mapped-by side to the mapping side cuts off -- that
|
||||||
|
* we don't traverse in a loop back to the mapped-by side in the generated
|
||||||
|
* SQL.
|
||||||
|
*
|
||||||
|
* @author Abe White
|
||||||
|
*/
|
||||||
|
public class TestEagerBidiSQL
|
||||||
|
extends SQLListenerTestCase {
|
||||||
|
|
||||||
|
private long id1;
|
||||||
|
private long id2;
|
||||||
|
|
||||||
|
public void setUp() {
|
||||||
|
setUp(BidiParent.class, BidiChild.class);
|
||||||
|
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
|
||||||
|
for (int i = 1; i <= 2; i++) {
|
||||||
|
BidiParent parent = new BidiParent();
|
||||||
|
parent.setName("parent" + i);
|
||||||
|
em.persist(parent);
|
||||||
|
|
||||||
|
BidiChild oneOneChild = new BidiChild();
|
||||||
|
oneOneChild.setName("oneToOneChild" + i);
|
||||||
|
oneOneChild.setOneToOneParent(parent);
|
||||||
|
parent.setOneToOneChild(oneOneChild);
|
||||||
|
em.persist(oneOneChild);
|
||||||
|
|
||||||
|
for (int j = 1; j <= 3; j++) {
|
||||||
|
BidiChild oneManyChild = new BidiChild();
|
||||||
|
oneManyChild.setName("oneToManyChild" + i + "::" + j);
|
||||||
|
oneManyChild.setOneToManyParent(parent);
|
||||||
|
parent.getOneToManyChildren().add(oneManyChild);
|
||||||
|
em.persist(oneManyChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 1)
|
||||||
|
id1 = parent.getId();
|
||||||
|
else
|
||||||
|
id2 = parent.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEagerOwnerOneToManyFind() {
|
||||||
|
sql.clear();
|
||||||
|
|
||||||
|
OpenJPAEntityManager em = emf.createEntityManager();
|
||||||
|
em.getFetchPlan().addField(BidiParent.class, "oneToManyChildren");
|
||||||
|
em.getFetchPlan().addField(BidiChild.class, "oneToManyParent");
|
||||||
|
BidiParent parent = em.find(BidiParent.class, id1);
|
||||||
|
assertEquals(1, sql.size());
|
||||||
|
assertNotSQL(".* LEFT OUTER JOIN BidiParent .*");
|
||||||
|
assertEquals("parent1", parent.getName());
|
||||||
|
assertEquals(3, parent.getOneToManyChildren().size());
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
assertEquals("oneToManyChild1::" + (i + 1),
|
||||||
|
parent.getOneToManyChildren().get(i).getName());
|
||||||
|
assertEquals(parent,
|
||||||
|
parent.getOneToManyChildren().get(i).getOneToManyParent());
|
||||||
|
}
|
||||||
|
assertEquals(1, sql.size());
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEagerOwnerOneToOneFind() {
|
||||||
|
sql.clear();
|
||||||
|
|
||||||
|
OpenJPAEntityManager em = emf.createEntityManager();
|
||||||
|
em.getFetchPlan().addField(BidiParent.class, "oneToOneChild");
|
||||||
|
em.getFetchPlan().addField(BidiChild.class, "oneToOneParent");
|
||||||
|
BidiParent parent = em.find(BidiParent.class, id1);
|
||||||
|
assertEquals(1, sql.size());
|
||||||
|
assertNotSQL(".* LEFT OUTER JOIN BidiParent .*");
|
||||||
|
assertEquals("parent1", parent.getName());
|
||||||
|
assertNotNull(parent.getOneToOneChild());
|
||||||
|
assertEquals("oneToOneChild1", parent.getOneToOneChild().getName());
|
||||||
|
assertEquals(parent, parent.getOneToOneChild().getOneToOneParent());
|
||||||
|
assertEquals(1, sql.size());
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEagerOwnerOneToManyQuery() {
|
||||||
|
sql.clear();
|
||||||
|
|
||||||
|
OpenJPAEntityManager em = emf.createEntityManager();
|
||||||
|
OpenJPAQuery q = em.createQuery("SELECT o FROM BidiParent o "
|
||||||
|
+ "ORDER BY o.name ASC");
|
||||||
|
q.getFetchPlan().addField(BidiParent.class, "oneToManyChildren");
|
||||||
|
q.getFetchPlan().addField(BidiChild.class, "oneToManyParent");
|
||||||
|
List<BidiParent> res = (List<BidiParent>) q.getResultList();
|
||||||
|
|
||||||
|
assertEquals(2, res.size());
|
||||||
|
assertEquals(sql.toString(), 2, sql.size());
|
||||||
|
assertNotSQL(".* LEFT OUTER JOIN BidiParent .*");
|
||||||
|
|
||||||
|
for (int i = 0; i < res.size(); i++) {
|
||||||
|
assertEquals("parent" + (i + 1), res.get(i).getName());
|
||||||
|
assertEquals(3, res.get(i).getOneToManyChildren().size());
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
assertEquals("oneToManyChild" + (i + 1) + "::" + (j + 1),
|
||||||
|
res.get(i).getOneToManyChildren().get(j).getName());
|
||||||
|
assertEquals(res.get(i), res.get(i).getOneToManyChildren().
|
||||||
|
get(j).getOneToManyParent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals(2, sql.size());
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEagerOwnerOneToOneQuery() {
|
||||||
|
sql.clear();
|
||||||
|
|
||||||
|
OpenJPAEntityManager em = emf.createEntityManager();
|
||||||
|
OpenJPAQuery q = em.createQuery("SELECT o FROM BidiParent o "
|
||||||
|
+ "ORDER BY o.name ASC");
|
||||||
|
q.getFetchPlan().addField(BidiParent.class, "oneToOneChild");
|
||||||
|
q.getFetchPlan().addField(BidiChild.class, "oneToOneParent");
|
||||||
|
List<BidiParent> res = (List<BidiParent>) q.getResultList();
|
||||||
|
|
||||||
|
assertEquals(2, res.size());
|
||||||
|
assertEquals(1, sql.size());
|
||||||
|
assertNotSQL(".* LEFT OUTER JOIN BidiParent .*");
|
||||||
|
|
||||||
|
for (int i = 0; i < res.size(); i++) {
|
||||||
|
assertEquals("parent" + (i + 1), res.get(i).getName());
|
||||||
|
assertNotNull(res.get(i).getOneToOneChild());
|
||||||
|
assertEquals("oneToOneChild" + (i + 1),
|
||||||
|
res.get(i).getOneToOneChild().getName());
|
||||||
|
assertEquals(res.get(i),
|
||||||
|
res.get(i).getOneToOneChild().getOneToOneParent());
|
||||||
|
}
|
||||||
|
assertEquals(1, sql.size());
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEagerNonOwnerOneToOneQuery() {
|
||||||
|
sql.clear();
|
||||||
|
|
||||||
|
OpenJPAEntityManager em = emf.createEntityManager();
|
||||||
|
OpenJPAQuery q = em.createQuery("SELECT o FROM BidiParent o "
|
||||||
|
+ "ORDER BY o.name ASC");
|
||||||
|
q.getFetchPlan().addField(BidiParent.class, "oneToOneChild");
|
||||||
|
q.getFetchPlan().addField(BidiChild.class, "oneToManyParent");
|
||||||
|
List<BidiParent> res = (List<BidiParent>) q.getResultList();
|
||||||
|
|
||||||
|
assertEquals(2, res.size());
|
||||||
|
assertEquals(1, sql.size());
|
||||||
|
assertSQL(".* LEFT OUTER JOIN BidiParent .*");
|
||||||
|
|
||||||
|
for (int i = 0; i < res.size(); i++) {
|
||||||
|
assertEquals("parent" + (i + 1), res.get(i).getName());
|
||||||
|
assertNotNull(res.get(i).getOneToOneChild());
|
||||||
|
assertEquals("oneToOneChild" + (i + 1),
|
||||||
|
res.get(i).getOneToOneChild().getName());
|
||||||
|
assertNull(res.get(i).getOneToOneChild().getOneToManyParent());
|
||||||
|
}
|
||||||
|
assertEquals(1, sql.size());
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
TestRunner.run(TestEagerBidiSQL.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -111,7 +111,8 @@ public final class ObjectData
|
||||||
|
|
||||||
FieldMetaData[] fmds = _meta.getFields();
|
FieldMetaData[] fmds = _meta.getFields();
|
||||||
for (int i = 0; i < fmds.length; i++)
|
for (int i = 0; i < fmds.length; i++)
|
||||||
if (!sm.getLoaded().get(i) && fetch.requiresFetch(fmds[i]))
|
if (!sm.getLoaded().get(i) && fetch.requiresFetch(fmds[i])
|
||||||
|
!= FetchConfiguration.FETCH_NONE)
|
||||||
sm.store(i, toLoadable(sm, fmds[i], _data[i], fetch));
|
sm.store(i, toLoadable(sm, fmds[i], _data[i], fetch));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue