HHH-2277 - bidirectional <key-many-to-one> both lazy=false fetch=join lead to infinite loop
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@19563 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
a58e1ef197
commit
460cbf8d96
|
@ -1022,9 +1022,11 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
|
||||
public Object internalLoad(String entityName, Serializable id, boolean eager, boolean nullable) throws HibernateException {
|
||||
// todo : remove
|
||||
LoadEventListener.LoadType type = nullable ?
|
||||
LoadEventListener.INTERNAL_LOAD_NULLABLE :
|
||||
eager ? LoadEventListener.INTERNAL_LOAD_EAGER : LoadEventListener.INTERNAL_LOAD_LAZY;
|
||||
LoadEventListener.LoadType type = nullable
|
||||
? LoadEventListener.INTERNAL_LOAD_NULLABLE
|
||||
: eager
|
||||
? LoadEventListener.INTERNAL_LOAD_EAGER
|
||||
: LoadEventListener.INTERNAL_LOAD_LAZY;
|
||||
LoadEvent event = new LoadEvent(id, entityName, true, this);
|
||||
fireLoad(event, type);
|
||||
if ( !nullable ) {
|
||||
|
|
|
@ -29,7 +29,6 @@ import java.util.List;
|
|||
import java.util.Iterator;
|
||||
|
||||
import org.hibernate.FetchMode;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.CascadeStyle;
|
||||
|
@ -42,7 +41,6 @@ import org.hibernate.persister.entity.OuterJoinLoadable;
|
|||
import org.hibernate.sql.JoinFragment;
|
||||
import org.hibernate.sql.Select;
|
||||
import org.hibernate.type.AssociationType;
|
||||
import org.hibernate.util.CollectionHelper;
|
||||
|
||||
/**
|
||||
* Abstract walker for walkers which begin at an entity (criteria
|
||||
|
@ -76,23 +74,20 @@ public abstract class AbstractEntityJoinWalker extends JoinWalker {
|
|||
final String whereString,
|
||||
final String orderByString,
|
||||
final LockOptions lockOptions) throws MappingException {
|
||||
initAll( whereString, orderByString, lockOptions, AssociationInitCallback.NO_CALLBACK );
|
||||
}
|
||||
|
||||
protected final void initAll(
|
||||
final String whereString,
|
||||
final String orderByString,
|
||||
final LockOptions lockOptions,
|
||||
final AssociationInitCallback callback) throws MappingException {
|
||||
walkEntityTree( persister, getAlias() );
|
||||
List allAssociations = new ArrayList();
|
||||
allAssociations.addAll(associations);
|
||||
allAssociations.add(
|
||||
new OuterJoinableAssociation(
|
||||
persister.getEntityType(),
|
||||
null,
|
||||
null,
|
||||
alias,
|
||||
JoinFragment.LEFT_OUTER_JOIN,
|
||||
null,
|
||||
getFactory(),
|
||||
CollectionHelper.EMPTY_MAP
|
||||
)
|
||||
);
|
||||
initPersisters(allAssociations, lockOptions);
|
||||
initStatementString( whereString, orderByString, lockOptions);
|
||||
allAssociations.addAll( associations );
|
||||
allAssociations.add( OuterJoinableAssociation.createRoot( persister.getEntityType(), alias, getFactory() ) );
|
||||
initPersisters( allAssociations, lockOptions, callback );
|
||||
initStatementString( whereString, orderByString, lockOptions );
|
||||
}
|
||||
|
||||
protected final void initProjection(
|
||||
|
@ -162,17 +157,18 @@ public abstract class AbstractEntityJoinWalker extends JoinWalker {
|
|||
return isJoinedFetchEnabledInMapping( config, type );
|
||||
}
|
||||
|
||||
protected final boolean isJoinFetchEnabledByProfile(OuterJoinLoadable persister, String path, int propertyNumber) {
|
||||
protected final boolean isJoinFetchEnabledByProfile(OuterJoinLoadable persister, PropertyPath path, int propertyNumber) {
|
||||
if ( !getLoadQueryInfluencers().hasEnabledFetchProfiles() ) {
|
||||
// perf optimization
|
||||
return false;
|
||||
}
|
||||
|
||||
// ugh, this stuff has to be made easier...
|
||||
final String fullPath = path.getFullPath();
|
||||
String rootPropertyName = persister.getSubclassPropertyName( propertyNumber );
|
||||
int pos = path.lastIndexOf( rootPropertyName );
|
||||
int pos = fullPath.lastIndexOf( rootPropertyName );
|
||||
String relativePropertyPath = pos >= 0
|
||||
? path.substring( pos )
|
||||
? fullPath.substring( pos )
|
||||
: rootPropertyName;
|
||||
String fetchRole = persister.getEntityName() + "." + relativePropertyPath;
|
||||
|
||||
|
|
|
@ -40,8 +40,6 @@ import org.hibernate.engine.CascadeStyle;
|
|||
import org.hibernate.engine.JoinHelper;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.profile.FetchProfile;
|
||||
import org.hibernate.engine.profile.Fetch;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
@ -93,7 +91,8 @@ public class JoinWalker {
|
|||
this.loadQueryInfluencers = loadQueryInfluencers;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String[] getCollectionSuffixes() {
|
||||
return collectionSuffixes;
|
||||
}
|
||||
|
@ -194,7 +193,7 @@ public class JoinWalker {
|
|||
final AssociationType type,
|
||||
final String[] aliasedLhsColumns,
|
||||
final String alias,
|
||||
final String path,
|
||||
final PropertyPath path,
|
||||
int currentDepth,
|
||||
final int joinType) throws MappingException {
|
||||
if ( joinType >= 0 ) {
|
||||
|
@ -209,7 +208,7 @@ public class JoinWalker {
|
|||
}
|
||||
}
|
||||
|
||||
protected String getWithClause(String path) {
|
||||
protected String getWithClause(PropertyPath path) {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -221,7 +220,7 @@ public class JoinWalker {
|
|||
final AssociationType type,
|
||||
final String[] aliasedLhsColumns,
|
||||
final String alias,
|
||||
String path,
|
||||
final PropertyPath path,
|
||||
final int currentDepth,
|
||||
final int joinType) throws MappingException {
|
||||
|
||||
|
@ -236,6 +235,7 @@ public class JoinWalker {
|
|||
// only need to worry about restrictions (and not say adding more
|
||||
// joins)
|
||||
OuterJoinableAssociation assoc = new OuterJoinableAssociation(
|
||||
path,
|
||||
type,
|
||||
alias,
|
||||
aliasedLhsColumns,
|
||||
|
@ -245,7 +245,7 @@ public class JoinWalker {
|
|||
getFactory(),
|
||||
loadQueryInfluencers.getEnabledFilters()
|
||||
);
|
||||
assoc.validateJoin( path );
|
||||
assoc.validateJoin( path.getFullPath() );
|
||||
associations.add( assoc );
|
||||
|
||||
int nextDepth = currentDepth + 1;
|
||||
|
@ -277,7 +277,7 @@ public class JoinWalker {
|
|||
* Walk the association tree for an entity, adding associations which should
|
||||
* be join fetched to the {@link #associations} inst var. This form is the
|
||||
* entry point into the walking for a given entity, starting the recursive
|
||||
* calls into {@link #walkEntityTree(OuterJoinLoadable, String, String, int)}.
|
||||
* calls into {@link #walkEntityTree(org.hibernate.persister.entity.OuterJoinLoadable, String, PropertyPath ,int)}.
|
||||
*
|
||||
* @param persister The persister representing the entity to be walked.
|
||||
* @param alias The (root) alias to use for this entity/persister.
|
||||
|
@ -286,15 +286,14 @@ public class JoinWalker {
|
|||
protected final void walkEntityTree(
|
||||
OuterJoinLoadable persister,
|
||||
String alias) throws MappingException {
|
||||
walkEntityTree( persister, alias, "", 0 );
|
||||
walkEntityTree( persister, alias, new PropertyPath(), 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* For a collection role, return a list of associations to be fetched by outerjoin
|
||||
*/
|
||||
protected final void walkCollectionTree(QueryableCollection persister, String alias)
|
||||
throws MappingException {
|
||||
walkCollectionTree(persister, alias, "", 0);
|
||||
protected final void walkCollectionTree(QueryableCollection persister, String alias) throws MappingException {
|
||||
walkCollectionTree( persister, alias, new PropertyPath(), 0 );
|
||||
//TODO: when this is the entry point, we should use an INNER_JOIN for fetching the many-to-many elements!
|
||||
}
|
||||
|
||||
|
@ -302,11 +301,10 @@ public class JoinWalker {
|
|||
* For a collection role, return a list of associations to be fetched by outerjoin
|
||||
*/
|
||||
private void walkCollectionTree(
|
||||
final QueryableCollection persister,
|
||||
final String alias,
|
||||
final String path,
|
||||
final int currentDepth)
|
||||
throws MappingException {
|
||||
final QueryableCollection persister,
|
||||
final String alias,
|
||||
final PropertyPath path,
|
||||
final int currentDepth) throws MappingException {
|
||||
|
||||
if ( persister.isOneToMany() ) {
|
||||
walkEntityTree(
|
||||
|
@ -338,7 +336,7 @@ public class JoinWalker {
|
|||
!useInnerJoin,
|
||||
currentDepth - 1,
|
||||
null //operations which cascade as far as the collection also cascade to collection elements
|
||||
);
|
||||
);
|
||||
addAssociationToJoinTreeIfNecessary(
|
||||
associationType,
|
||||
aliasedLhsColumns,
|
||||
|
@ -382,7 +380,7 @@ public class JoinWalker {
|
|||
final OuterJoinLoadable persister,
|
||||
final int propertyNumber,
|
||||
final String alias,
|
||||
final String path,
|
||||
final PropertyPath path,
|
||||
final boolean nullable,
|
||||
final int currentDepth) throws MappingException {
|
||||
String[] aliasedLhsColumns = JoinHelper.getAliasedLHSColumnNames(
|
||||
|
@ -393,10 +391,10 @@ public class JoinWalker {
|
|||
);
|
||||
String lhsTable = JoinHelper.getLHSTableName(associationType, propertyNumber, persister);
|
||||
|
||||
String subpath = subPath( path, persister.getSubclassPropertyName(propertyNumber) );
|
||||
PropertyPath subPath = path.append( persister.getSubclassPropertyName(propertyNumber) );
|
||||
int joinType = getJoinType(
|
||||
persister,
|
||||
subpath,
|
||||
subPath,
|
||||
propertyNumber,
|
||||
associationType,
|
||||
persister.getFetchMode( propertyNumber ),
|
||||
|
@ -410,7 +408,7 @@ public class JoinWalker {
|
|||
associationType,
|
||||
aliasedLhsColumns,
|
||||
alias,
|
||||
subpath,
|
||||
subPath,
|
||||
currentDepth,
|
||||
joinType
|
||||
);
|
||||
|
@ -436,7 +434,7 @@ public class JoinWalker {
|
|||
*/
|
||||
protected int getJoinType(
|
||||
OuterJoinLoadable persister,
|
||||
final String path,
|
||||
final PropertyPath path,
|
||||
int propertyNumber,
|
||||
AssociationType associationType,
|
||||
FetchMode metadataFetchMode,
|
||||
|
@ -476,7 +474,7 @@ public class JoinWalker {
|
|||
protected int getJoinType(
|
||||
AssociationType associationType,
|
||||
FetchMode config,
|
||||
String path,
|
||||
PropertyPath path,
|
||||
String lhsTable,
|
||||
String[] lhsColumns,
|
||||
boolean nullable,
|
||||
|
@ -498,18 +496,18 @@ public class JoinWalker {
|
|||
* Walk the association tree for an entity, adding associations which should
|
||||
* be join fetched to the {@link #associations} inst var. This form is the
|
||||
* entry point into the walking for a given entity, starting the recursive
|
||||
* calls into {@link #walkEntityTree(OuterJoinLoadable, String, String, int)}.
|
||||
* calls into {@link #walkEntityTree(org.hibernate.persister.entity.OuterJoinLoadable, String, PropertyPath ,int)}.
|
||||
*
|
||||
* @param persister The persister representing the entity to be walked.
|
||||
* @param alias The (root) alias to use for this entity/persister.
|
||||
* @param path todo this seems to be rooted at the *root* persister
|
||||
* @param path The property path to the entity being walked
|
||||
* @param currentDepth The current join depth
|
||||
* @throws org.hibernate.MappingException ???
|
||||
*/
|
||||
private void walkEntityTree(
|
||||
final OuterJoinLoadable persister,
|
||||
final String alias,
|
||||
final String path,
|
||||
final PropertyPath path,
|
||||
final int currentDepth) throws MappingException {
|
||||
int n = persister.countSubclassProperties();
|
||||
for ( int i = 0; i < n; i++ ) {
|
||||
|
@ -527,13 +525,13 @@ public class JoinWalker {
|
|||
}
|
||||
else if ( type.isComponentType() ) {
|
||||
walkComponentTree(
|
||||
( AbstractComponentType ) type,
|
||||
i,
|
||||
0,
|
||||
persister,
|
||||
alias,
|
||||
subPath( path, persister.getSubclassPropertyName(i) ),
|
||||
currentDepth
|
||||
( AbstractComponentType ) type,
|
||||
i,
|
||||
0,
|
||||
persister,
|
||||
alias,
|
||||
path.append( persister.getSubclassPropertyName(i) ),
|
||||
currentDepth
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -559,7 +557,7 @@ public class JoinWalker {
|
|||
int begin,
|
||||
final OuterJoinLoadable persister,
|
||||
final String alias,
|
||||
final String path,
|
||||
final PropertyPath path,
|
||||
final int currentDepth) throws MappingException {
|
||||
Type[] types = componentType.getSubtypes();
|
||||
String[] propertyNames = componentType.getPropertyNames();
|
||||
|
@ -574,11 +572,11 @@ public class JoinWalker {
|
|||
);
|
||||
String lhsTable = JoinHelper.getLHSTableName(associationType, propertyNumber, persister);
|
||||
|
||||
String subpath = subPath( path, propertyNames[i] );
|
||||
final PropertyPath subPath = path.append( propertyNames[i] );
|
||||
final boolean[] propertyNullability = componentType.getPropertyNullability();
|
||||
final int joinType = getJoinType(
|
||||
persister,
|
||||
subpath,
|
||||
subPath,
|
||||
propertyNumber,
|
||||
associationType,
|
||||
componentType.getFetchMode(i),
|
||||
|
@ -592,21 +590,21 @@ public class JoinWalker {
|
|||
associationType,
|
||||
aliasedLhsColumns,
|
||||
alias,
|
||||
subpath,
|
||||
subPath,
|
||||
currentDepth,
|
||||
joinType
|
||||
);
|
||||
|
||||
}
|
||||
else if ( types[i].isComponentType() ) {
|
||||
String subpath = subPath( path, propertyNames[i] );
|
||||
final PropertyPath subPath = path.append( propertyNames[i] );
|
||||
walkComponentTree(
|
||||
( AbstractComponentType ) types[i],
|
||||
propertyNumber,
|
||||
begin,
|
||||
persister,
|
||||
alias,
|
||||
subpath,
|
||||
subPath,
|
||||
currentDepth
|
||||
);
|
||||
}
|
||||
|
@ -623,7 +621,7 @@ public class JoinWalker {
|
|||
final String[] cols,
|
||||
final QueryableCollection persister,
|
||||
final String alias,
|
||||
final String path,
|
||||
final PropertyPath path,
|
||||
final int currentDepth) throws MappingException {
|
||||
|
||||
Type[] types = compositeType.getSubtypes();
|
||||
|
@ -640,12 +638,12 @@ public class JoinWalker {
|
|||
// (or even a property-ref) in a composite-element:
|
||||
String[] aliasedLhsColumns = StringHelper.qualify(alias, lhsColumns);
|
||||
|
||||
String subpath = subPath( path, propertyNames[i] );
|
||||
final PropertyPath subPath = path.append( propertyNames[i] );
|
||||
final boolean[] propertyNullability = compositeType.getPropertyNullability();
|
||||
final int joinType = getJoinType(
|
||||
associationType,
|
||||
compositeType.getFetchMode(i),
|
||||
subpath,
|
||||
subPath,
|
||||
persister.getTableName(),
|
||||
lhsColumns,
|
||||
propertyNullability==null || propertyNullability[i],
|
||||
|
@ -656,19 +654,19 @@ public class JoinWalker {
|
|||
associationType,
|
||||
aliasedLhsColumns,
|
||||
alias,
|
||||
subpath,
|
||||
subPath,
|
||||
currentDepth,
|
||||
joinType
|
||||
);
|
||||
}
|
||||
else if ( types[i].isComponentType() ) {
|
||||
String subpath = subPath( path, propertyNames[i] );
|
||||
final PropertyPath subPath = path.append( propertyNames[i] );
|
||||
walkCompositeElementTree(
|
||||
(AbstractComponentType) types[i],
|
||||
lhsColumns,
|
||||
persister,
|
||||
alias,
|
||||
subpath,
|
||||
subPath,
|
||||
currentDepth
|
||||
);
|
||||
}
|
||||
|
@ -677,18 +675,6 @@ public class JoinWalker {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the path by the given property name
|
||||
*/
|
||||
private static String subPath(String path, String property) {
|
||||
if ( path==null || path.length()==0) {
|
||||
return property;
|
||||
}
|
||||
else {
|
||||
return StringHelper.qualify(path, property);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use an inner join if it is a non-null association and this
|
||||
* is the "first" join in a series
|
||||
|
@ -746,10 +732,7 @@ public class JoinWalker {
|
|||
return type.isEntityType() && isJoinedFetchEnabledInMapping(config, type) ;
|
||||
}
|
||||
|
||||
protected String generateTableAlias(
|
||||
final int n,
|
||||
final String path,
|
||||
final Joinable joinable) {
|
||||
protected String generateTableAlias(final int n, final PropertyPath path, final Joinable joinable) {
|
||||
return StringHelper.generateAlias( joinable.getName(), n );
|
||||
}
|
||||
|
||||
|
@ -761,10 +744,7 @@ public class JoinWalker {
|
|||
* Used to detect circularities in the joined graph, note that
|
||||
* this method is side-effecty
|
||||
*/
|
||||
protected boolean isDuplicateAssociation(
|
||||
final String foreignKeyTable,
|
||||
final String[] foreignKeyColumns
|
||||
) {
|
||||
protected boolean isDuplicateAssociation(final String foreignKeyTable, final String[] foreignKeyColumns) {
|
||||
AssociationKey associationKey = new AssociationKey(foreignKeyColumns, foreignKeyTable);
|
||||
return !visitedAssociationKeys.add( associationKey );
|
||||
}
|
||||
|
@ -773,11 +753,7 @@ public class JoinWalker {
|
|||
* Used to detect circularities in the joined graph, note that
|
||||
* this method is side-effecty
|
||||
*/
|
||||
protected boolean isDuplicateAssociation(
|
||||
final String lhsTable,
|
||||
final String[] lhsColumnNames,
|
||||
final AssociationType type
|
||||
) {
|
||||
protected boolean isDuplicateAssociation(final String lhsTable, final String[] lhsColumnNames, final AssociationType type) {
|
||||
final String foreignKeyTable;
|
||||
final String[] foreignKeyColumns;
|
||||
if ( type.getForeignKeyDirection()==ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT ) {
|
||||
|
@ -815,20 +791,23 @@ public class JoinWalker {
|
|||
* Should we join this association?
|
||||
*/
|
||||
protected boolean isJoinable(
|
||||
final int joinType,
|
||||
final Set visitedAssociationKeys,
|
||||
final String lhsTable,
|
||||
final String[] lhsColumnNames,
|
||||
final AssociationType type,
|
||||
final int depth
|
||||
) {
|
||||
if (joinType<0) return false;
|
||||
|
||||
if (joinType==JoinFragment.INNER_JOIN) return true;
|
||||
final int joinType,
|
||||
final Set visitedAssociationKeys,
|
||||
final String lhsTable,
|
||||
final String[] lhsColumnNames,
|
||||
final AssociationType type,
|
||||
final int depth) {
|
||||
|
||||
if ( joinType < 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( joinType == JoinFragment.INNER_JOIN ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Integer maxFetchDepth = getFactory().getSettings().getMaximumFetchDepth();
|
||||
final boolean tooDeep = maxFetchDepth!=null &&
|
||||
depth >= maxFetchDepth.intValue();
|
||||
final boolean tooDeep = maxFetchDepth!=null && depth >= maxFetchDepth.intValue();
|
||||
|
||||
return !tooDeep && !isDuplicateAssociation(lhsTable, lhsColumnNames, type);
|
||||
}
|
||||
|
@ -985,8 +964,22 @@ public class JoinWalker {
|
|||
initPersisters( associations, new LockOptions(lockMode));
|
||||
}
|
||||
|
||||
protected static interface AssociationInitCallback {
|
||||
public static final AssociationInitCallback NO_CALLBACK = new AssociationInitCallback() {
|
||||
public void associationProcessed(OuterJoinableAssociation oja, int position) {
|
||||
}
|
||||
};
|
||||
|
||||
public void associationProcessed(OuterJoinableAssociation oja, int position);
|
||||
}
|
||||
protected void initPersisters(final List associations, final LockOptions lockOptions) throws MappingException {
|
||||
|
||||
initPersisters( associations, lockOptions, AssociationInitCallback.NO_CALLBACK );
|
||||
}
|
||||
|
||||
protected void initPersisters(
|
||||
final List associations,
|
||||
final LockOptions lockOptions,
|
||||
final AssociationInitCallback callback) throws MappingException {
|
||||
final int joins = countEntityPersisters(associations);
|
||||
final int collections = countCollectionPersisters(associations);
|
||||
|
||||
|
@ -1013,6 +1006,7 @@ public class JoinWalker {
|
|||
aliases[i] = oj.getRHSAlias();
|
||||
owners[i] = oj.getOwner(associations);
|
||||
ownerAssociationTypes[i] = (EntityType) oj.getJoinableType();
|
||||
callback.associationProcessed( oj, i );
|
||||
i++;
|
||||
|
||||
}
|
||||
|
@ -1029,11 +1023,12 @@ public class JoinWalker {
|
|||
if ( collPersister.isOneToMany() ) {
|
||||
persisters[i] = (Loadable) collPersister.getElementPersister();
|
||||
aliases[i] = oj.getRHSAlias();
|
||||
callback.associationProcessed( oj, i );
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( ArrayHelper.isAllNegative(owners) ) owners = null;
|
||||
if ( collectionOwners!=null && ArrayHelper.isAllNegative(collectionOwners) ) {
|
||||
collectionOwners = null;
|
||||
|
|
|
@ -173,6 +173,10 @@ public abstract class Loader {
|
|||
return null;
|
||||
}
|
||||
|
||||
protected int[][] getCompositeKeyManyToOneTargetIndices() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* What lock options does this load entities with?
|
||||
*
|
||||
|
@ -599,19 +603,7 @@ public abstract class Loader {
|
|||
|
||||
final Loadable[] persisters = getEntityPersisters();
|
||||
final int entitySpan = persisters.length;
|
||||
|
||||
for ( int i = 0; i < entitySpan; i++ ) {
|
||||
keys[i] = getKeyFromResultSet(
|
||||
i,
|
||||
persisters[i],
|
||||
i == entitySpan - 1 ?
|
||||
queryParameters.getOptionalId() :
|
||||
null,
|
||||
resultSet,
|
||||
session
|
||||
);
|
||||
//TODO: the i==entitySpan-1 bit depends upon subclass implementation (very bad)
|
||||
}
|
||||
extractKeysFromResultSet( persisters, queryParameters, resultSet, session, keys, lockModesArray, hydratedObjects );
|
||||
|
||||
registerNonExists( keys, persisters, session );
|
||||
|
||||
|
@ -648,6 +640,98 @@ public abstract class Loader {
|
|||
|
||||
}
|
||||
|
||||
protected void extractKeysFromResultSet(
|
||||
Loadable[] persisters,
|
||||
QueryParameters queryParameters,
|
||||
ResultSet resultSet,
|
||||
SessionImplementor session,
|
||||
EntityKey[] keys,
|
||||
LockMode[] lockModes,
|
||||
List hydratedObjects) throws SQLException {
|
||||
final int entitySpan = persisters.length;
|
||||
|
||||
final int numberOfPersistersToProcess;
|
||||
final Serializable optionalId = queryParameters.getOptionalId();
|
||||
if ( isSingleRowLoader() && optionalId != null ) {
|
||||
keys[ entitySpan - 1 ] = new EntityKey( optionalId, persisters[ entitySpan - 1 ], session.getEntityMode() );
|
||||
// skip the last persister below...
|
||||
numberOfPersistersToProcess = entitySpan - 1;
|
||||
}
|
||||
else {
|
||||
numberOfPersistersToProcess = entitySpan;
|
||||
}
|
||||
|
||||
final Object[] hydratedKeyState = new Object[numberOfPersistersToProcess];
|
||||
|
||||
for ( int i = 0; i < numberOfPersistersToProcess; i++ ) {
|
||||
final Type idType = persisters[i].getIdentifierType();
|
||||
hydratedKeyState[i] = idType.hydrate( resultSet, getEntityAliases()[i].getSuffixedKeyAliases(), session, null );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < numberOfPersistersToProcess; i++ ) {
|
||||
final Type idType = persisters[i].getIdentifierType();
|
||||
if ( idType.isComponentType() && getCompositeKeyManyToOneTargetIndices() != null ) {
|
||||
// we may need to force resolve any key-many-to-one(s)
|
||||
int[] keyManyToOneTargetIndices = getCompositeKeyManyToOneTargetIndices()[i];
|
||||
// todo : better solution is to order the index processing based on target indices
|
||||
// that would account for multiple levels whereas this scheme does not
|
||||
if ( keyManyToOneTargetIndices != null ) {
|
||||
for ( int targetIndex : keyManyToOneTargetIndices ) {
|
||||
if ( targetIndex < numberOfPersistersToProcess ) {
|
||||
final Type targetIdType = persisters[targetIndex].getIdentifierType();
|
||||
final Serializable targetId = (Serializable) targetIdType.resolve(
|
||||
hydratedKeyState[targetIndex],
|
||||
session,
|
||||
null
|
||||
);
|
||||
// todo : need a way to signal that this key is resolved and its data resolved
|
||||
keys[targetIndex] = new EntityKey( targetId, persisters[targetIndex], session.getEntityMode() );
|
||||
}
|
||||
|
||||
// this part copied from #getRow, this section could be refactored out
|
||||
Object object = session.getEntityUsingInterceptor( keys[targetIndex] );
|
||||
if ( object != null ) {
|
||||
//its already loaded so don't need to hydrate it
|
||||
instanceAlreadyLoaded(
|
||||
resultSet,
|
||||
targetIndex,
|
||||
persisters[targetIndex],
|
||||
keys[targetIndex],
|
||||
object,
|
||||
lockModes[targetIndex],
|
||||
session
|
||||
);
|
||||
}
|
||||
else {
|
||||
object = instanceNotYetLoaded(
|
||||
resultSet,
|
||||
targetIndex,
|
||||
persisters[targetIndex],
|
||||
getEntityAliases()[targetIndex].getRowIdAlias(),
|
||||
keys[targetIndex],
|
||||
lockModes[targetIndex],
|
||||
getOptionalObjectKey( queryParameters, session ),
|
||||
queryParameters.getOptionalObject(),
|
||||
hydratedObjects,
|
||||
session
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
final Serializable resolvedId = (Serializable) idType.resolve( hydratedKeyState[i], session, null );
|
||||
keys[i] = resolvedId == null ? null : new EntityKey( resolvedId, persisters[i], session.getEntityMode() );
|
||||
}
|
||||
}
|
||||
|
||||
private Serializable determineResultId(SessionImplementor session, Serializable optionalId, Type idType, Serializable resolvedId) {
|
||||
final boolean idIsResultId = optionalId != null
|
||||
&& resolvedId != null
|
||||
&& idType.isEqual( optionalId, resolvedId, session.getEntityMode(), factory );
|
||||
final Serializable resultId = idIsResultId ? optionalId : resolvedId;
|
||||
return resultId;
|
||||
}
|
||||
|
||||
protected void applyPostLoadLocks(Object[] row, LockMode[] lockModesArray, SessionImplementor session) {
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
|
||||
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Middleware LLC.
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
|
@ -20,7 +20,6 @@
|
|||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
package org.hibernate.loader;
|
||||
|
||||
|
@ -35,6 +34,7 @@ import org.hibernate.persister.entity.Joinable;
|
|||
import org.hibernate.sql.JoinFragment;
|
||||
import org.hibernate.type.AssociationType;
|
||||
import org.hibernate.type.EntityType;
|
||||
import org.hibernate.util.CollectionHelper;
|
||||
|
||||
/**
|
||||
* Part of the Hibernate SQL rendering internals. This class represents
|
||||
|
@ -43,6 +43,7 @@ import org.hibernate.type.EntityType;
|
|||
* @author Gavin King
|
||||
*/
|
||||
public final class OuterJoinableAssociation {
|
||||
private final PropertyPath propertyPath;
|
||||
private final AssociationType joinableType;
|
||||
private final Joinable joinable;
|
||||
private final String lhsAlias; // belong to other persister
|
||||
|
@ -53,7 +54,25 @@ public final class OuterJoinableAssociation {
|
|||
private final String on;
|
||||
private final Map enabledFilters;
|
||||
|
||||
public static OuterJoinableAssociation createRoot(
|
||||
AssociationType joinableType,
|
||||
String alias,
|
||||
SessionFactoryImplementor factory) {
|
||||
return new OuterJoinableAssociation(
|
||||
new PropertyPath(),
|
||||
joinableType,
|
||||
null,
|
||||
null,
|
||||
alias,
|
||||
JoinFragment.LEFT_OUTER_JOIN,
|
||||
null,
|
||||
factory,
|
||||
CollectionHelper.EMPTY_MAP
|
||||
);
|
||||
}
|
||||
|
||||
public OuterJoinableAssociation(
|
||||
PropertyPath propertyPath,
|
||||
AssociationType joinableType,
|
||||
String lhsAlias,
|
||||
String[] lhsColumns,
|
||||
|
@ -62,6 +81,7 @@ public final class OuterJoinableAssociation {
|
|||
String withClause,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters) throws MappingException {
|
||||
this.propertyPath = propertyPath;
|
||||
this.joinableType = joinableType;
|
||||
this.lhsAlias = lhsAlias;
|
||||
this.lhsColumns = lhsColumns;
|
||||
|
@ -74,14 +94,26 @@ public final class OuterJoinableAssociation {
|
|||
this.enabledFilters = enabledFilters; // needed later for many-to-many/filter application
|
||||
}
|
||||
|
||||
public PropertyPath getPropertyPath() {
|
||||
return propertyPath;
|
||||
}
|
||||
|
||||
public int getJoinType() {
|
||||
return joinType;
|
||||
}
|
||||
|
||||
public String getLhsAlias() {
|
||||
return lhsAlias;
|
||||
}
|
||||
|
||||
public String getRHSAlias() {
|
||||
return rhsAlias;
|
||||
}
|
||||
|
||||
public String getRhsAlias() {
|
||||
return rhsAlias;
|
||||
}
|
||||
|
||||
private boolean isOneToOne() {
|
||||
if ( joinableType.isEntityType() ) {
|
||||
EntityType etype = (EntityType) joinableType;
|
||||
|
@ -90,7 +122,6 @@ public final class OuterJoinableAssociation {
|
|||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AssociationType getJoinableType() {
|
||||
|
@ -150,12 +181,8 @@ public final class OuterJoinableAssociation {
|
|||
}
|
||||
|
||||
public void validateJoin(String path) throws MappingException {
|
||||
if (
|
||||
rhsColumns==null ||
|
||||
lhsColumns==null ||
|
||||
lhsColumns.length!=rhsColumns.length ||
|
||||
lhsColumns.length==0
|
||||
) {
|
||||
if ( rhsColumns==null || lhsColumns==null
|
||||
|| lhsColumns.length!=rhsColumns.length || lhsColumns.length==0 ) {
|
||||
throw new MappingException("invalid join columns for association: " + path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.loader;
|
||||
|
||||
import org.hibernate.util.StringHelper;
|
||||
|
||||
/**
|
||||
* TODO : javadoc
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class PropertyPath {
|
||||
private final PropertyPath parent;
|
||||
private final String property;
|
||||
private final String fullPath;
|
||||
|
||||
public PropertyPath(PropertyPath parent, String property) {
|
||||
this.parent = parent;
|
||||
this.property = property;
|
||||
|
||||
final String prefix;
|
||||
if ( parent != null ) {
|
||||
final String resolvedParent = parent.getFullPath();
|
||||
if ( StringHelper.isEmpty( resolvedParent ) ) {
|
||||
prefix = "";
|
||||
}
|
||||
else {
|
||||
prefix = resolvedParent + '.';
|
||||
}
|
||||
}
|
||||
else {
|
||||
prefix = "";
|
||||
}
|
||||
this.fullPath = prefix + property;
|
||||
}
|
||||
|
||||
public PropertyPath(String property) {
|
||||
this( null, property );
|
||||
}
|
||||
|
||||
public PropertyPath() {
|
||||
this( "" );
|
||||
}
|
||||
|
||||
public PropertyPath append(String property) {
|
||||
return new PropertyPath( this, property );
|
||||
}
|
||||
|
||||
public PropertyPath getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
public String getFullPath() {
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
public boolean isRoot() {
|
||||
return parent == null && StringHelper.isEmpty( property );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + '[' + fullPath + ']';
|
||||
}
|
||||
}
|
|
@ -27,8 +27,6 @@ package org.hibernate.loader.collection;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.FetchMode;
|
||||
import org.hibernate.LockMode;
|
||||
|
@ -38,12 +36,12 @@ import org.hibernate.engine.LoadQueryInfluencers;
|
|||
import org.hibernate.engine.CascadeStyle;
|
||||
import org.hibernate.loader.BasicLoader;
|
||||
import org.hibernate.loader.OuterJoinableAssociation;
|
||||
import org.hibernate.loader.PropertyPath;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.sql.JoinFragment;
|
||||
import org.hibernate.sql.Select;
|
||||
import org.hibernate.type.AssociationType;
|
||||
import org.hibernate.util.CollectionHelper;
|
||||
import org.hibernate.util.StringHelper;
|
||||
|
||||
/**
|
||||
|
@ -73,18 +71,7 @@ public class BasicCollectionJoinWalker extends CollectionJoinWalker {
|
|||
|
||||
List allAssociations = new ArrayList();
|
||||
allAssociations.addAll(associations);
|
||||
allAssociations.add(
|
||||
new OuterJoinableAssociation(
|
||||
collectionPersister.getCollectionType(),
|
||||
null,
|
||||
null,
|
||||
alias,
|
||||
JoinFragment.LEFT_OUTER_JOIN,
|
||||
null,
|
||||
getFactory(),
|
||||
CollectionHelper.EMPTY_MAP
|
||||
)
|
||||
);
|
||||
allAssociations.add( OuterJoinableAssociation.createRoot( collectionPersister.getCollectionType(), alias, getFactory() ) );
|
||||
initPersisters(allAssociations, LockMode.NONE);
|
||||
initStatementString(alias, batchSize, subquery);
|
||||
}
|
||||
|
@ -155,7 +142,7 @@ public class BasicCollectionJoinWalker extends CollectionJoinWalker {
|
|||
|
||||
protected int getJoinType(
|
||||
OuterJoinLoadable persister,
|
||||
String path,
|
||||
PropertyPath path,
|
||||
int propertyNumber,
|
||||
AssociationType associationType,
|
||||
FetchMode metadataFetchMode,
|
||||
|
@ -177,7 +164,7 @@ public class BasicCollectionJoinWalker extends CollectionJoinWalker {
|
|||
currentDepth
|
||||
);
|
||||
//we can use an inner join for the many-to-many
|
||||
if ( joinType==JoinFragment.LEFT_OUTER_JOIN && "".equals(path) ) {
|
||||
if ( joinType==JoinFragment.LEFT_OUTER_JOIN && path.isRoot() ) {
|
||||
joinType=JoinFragment.INNER_JOIN;
|
||||
}
|
||||
return joinType;
|
||||
|
|
|
@ -79,17 +79,7 @@ public class OneToManyJoinWalker extends CollectionJoinWalker {
|
|||
|
||||
List allAssociations = new ArrayList();
|
||||
allAssociations.addAll(associations);
|
||||
allAssociations.add( new OuterJoinableAssociation(
|
||||
oneToManyPersister.getCollectionType(),
|
||||
null,
|
||||
null,
|
||||
alias,
|
||||
JoinFragment.LEFT_OUTER_JOIN,
|
||||
null,
|
||||
getFactory(),
|
||||
CollectionHelper.EMPTY_MAP
|
||||
) );
|
||||
|
||||
allAssociations.add( OuterJoinableAssociation.createRoot( oneToManyPersister.getCollectionType(), alias, getFactory() ) );
|
||||
initPersisters(allAssociations, LockMode.NONE);
|
||||
initStatementString(elementPersister, alias, batchSize, subquery);
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ import java.util.Set;
|
|||
|
||||
import org.hibernate.Criteria;
|
||||
import org.hibernate.FetchMode;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.CascadeStyle;
|
||||
|
@ -38,12 +37,12 @@ import org.hibernate.engine.SessionFactoryImplementor;
|
|||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.impl.CriteriaImpl;
|
||||
import org.hibernate.loader.AbstractEntityJoinWalker;
|
||||
import org.hibernate.loader.PropertyPath;
|
||||
import org.hibernate.persister.entity.Joinable;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
import org.hibernate.type.AssociationType;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.type.TypeFactory;
|
||||
import org.hibernate.util.ArrayHelper;
|
||||
|
||||
/**
|
||||
|
@ -121,7 +120,7 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
|
|||
|
||||
protected int getJoinType(
|
||||
OuterJoinLoadable persister,
|
||||
final String path,
|
||||
final PropertyPath path,
|
||||
int propertyNumber,
|
||||
AssociationType associationType,
|
||||
FetchMode metadataFetchMode,
|
||||
|
@ -130,15 +129,15 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
|
|||
String[] lhsColumns,
|
||||
final boolean nullable,
|
||||
final int currentDepth) throws MappingException {
|
||||
if ( translator.isJoin( path ) ) {
|
||||
return translator.getJoinType( path );
|
||||
if ( translator.isJoin( path.getFullPath() ) ) {
|
||||
return translator.getJoinType( path.getFullPath() );
|
||||
}
|
||||
else {
|
||||
if ( translator.hasProjection() ) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
FetchMode fetchMode = translator.getRootCriteria().getFetchMode( path );
|
||||
FetchMode fetchMode = translator.getRootCriteria().getFetchMode( path.getFullPath() );
|
||||
if ( isDefaultFetchMode( fetchMode ) ) {
|
||||
if ( isJoinFetchEnabledByProfile( persister, path, propertyNumber ) ) {
|
||||
return getJoinType( nullable, currentDepth );
|
||||
|
@ -174,14 +173,14 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
|
|||
protected int getJoinType(
|
||||
AssociationType associationType,
|
||||
FetchMode config,
|
||||
String path,
|
||||
PropertyPath path,
|
||||
String lhsTable,
|
||||
String[] lhsColumns,
|
||||
boolean nullable,
|
||||
int currentDepth,
|
||||
CascadeStyle cascadeStyle) throws MappingException {
|
||||
return ( translator.isJoin( path ) ?
|
||||
translator.getJoinType( path ) :
|
||||
return ( translator.isJoin( path.getFullPath() ) ?
|
||||
translator.getJoinType( path.getFullPath() ) :
|
||||
super.getJoinType(
|
||||
associationType,
|
||||
config,
|
||||
|
@ -208,9 +207,9 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
|
|||
( (Queryable) getPersister() ).filterFragment( getAlias(), getLoadQueryInfluencers().getEnabledFilters() );
|
||||
}
|
||||
|
||||
protected String generateTableAlias(int n, String path, Joinable joinable) {
|
||||
protected String generateTableAlias(int n, PropertyPath path, Joinable joinable) {
|
||||
if ( joinable.consumesEntityAlias() ) {
|
||||
final Criteria subcriteria = translator.getCriteria(path);
|
||||
final Criteria subcriteria = translator.getCriteria( path.getFullPath() );
|
||||
String sqlAlias = subcriteria==null ? null : translator.getSQLAlias(subcriteria);
|
||||
if (sqlAlias!=null) {
|
||||
userAliasList.add( subcriteria.getAlias() ); //alias may be null
|
||||
|
@ -235,8 +234,8 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
|
|||
return "criteria query";
|
||||
}
|
||||
|
||||
protected String getWithClause(String path) {
|
||||
return translator.getWithClause(path);
|
||||
protected String getWithClause(PropertyPath path) {
|
||||
return translator.getWithClause( path.getFullPath() );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,18 +24,27 @@
|
|||
*/
|
||||
package org.hibernate.loader.entity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.hibernate.FetchMode;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.CascadeStyle;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.loader.AbstractEntityJoinWalker;
|
||||
import org.hibernate.loader.OuterJoinableAssociation;
|
||||
import org.hibernate.loader.PropertyPath;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.type.AssociationType;
|
||||
import org.hibernate.type.ComponentType;
|
||||
import org.hibernate.type.EntityType;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* A walker for loaders that fetch entities
|
||||
|
@ -46,13 +55,14 @@ import org.hibernate.type.AssociationType;
|
|||
public class EntityJoinWalker extends AbstractEntityJoinWalker {
|
||||
|
||||
private final LockOptions lockOptions = new LockOptions();
|
||||
private final int[][] compositeKeyManyToOneTargetIndices;
|
||||
|
||||
public EntityJoinWalker(
|
||||
OuterJoinLoadable persister,
|
||||
String[] uniqueKey,
|
||||
int batchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
final SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
super( persister, factory, loadQueryInfluencers );
|
||||
|
||||
|
@ -62,7 +72,9 @@ public class EntityJoinWalker extends AbstractEntityJoinWalker {
|
|||
//include the discriminator and class-level where, but not filters
|
||||
.append( persister.filterFragment( getAlias(), Collections.EMPTY_MAP ) );
|
||||
|
||||
initAll( whereCondition.toString(), "", lockOptions);
|
||||
AssociationInitCallbackImpl callback = new AssociationInitCallbackImpl( factory );
|
||||
initAll( whereCondition.toString(), "", lockOptions, callback );
|
||||
this.compositeKeyManyToOneTargetIndices = callback.resolve();
|
||||
}
|
||||
|
||||
public EntityJoinWalker(
|
||||
|
@ -79,12 +91,14 @@ public class EntityJoinWalker extends AbstractEntityJoinWalker {
|
|||
//include the discriminator and class-level where, but not filters
|
||||
.append( persister.filterFragment( getAlias(), Collections.EMPTY_MAP ) );
|
||||
|
||||
initAll( whereCondition.toString(), "", lockOptions);
|
||||
AssociationInitCallbackImpl callback = new AssociationInitCallbackImpl( factory );
|
||||
initAll( whereCondition.toString(), "", lockOptions, callback );
|
||||
this.compositeKeyManyToOneTargetIndices = callback.resolve();
|
||||
}
|
||||
|
||||
protected int getJoinType(
|
||||
OuterJoinLoadable persister,
|
||||
String path,
|
||||
PropertyPath path,
|
||||
int propertyNumber,
|
||||
AssociationType associationType,
|
||||
FetchMode metadataFetchMode,
|
||||
|
@ -116,5 +130,132 @@ public class EntityJoinWalker extends AbstractEntityJoinWalker {
|
|||
public String getComment() {
|
||||
return "load " + getPersister().getEntityName();
|
||||
}
|
||||
|
||||
|
||||
public int[][] getCompositeKeyManyToOneTargetIndices() {
|
||||
return compositeKeyManyToOneTargetIndices;
|
||||
}
|
||||
|
||||
private static class AssociationInitCallbackImpl implements AssociationInitCallback {
|
||||
private final SessionFactoryImplementor factory;
|
||||
private final HashMap<String,OuterJoinableAssociation> associationsByAlias
|
||||
= new HashMap<String, OuterJoinableAssociation>();
|
||||
private final HashMap<String,Integer> positionsByAlias = new HashMap<String, Integer>();
|
||||
private final ArrayList<String> aliasesForAssociationsWithCompositesIds
|
||||
= new ArrayList<String>();
|
||||
|
||||
public AssociationInitCallbackImpl(SessionFactoryImplementor factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public void associationProcessed(OuterJoinableAssociation oja, int position) {
|
||||
associationsByAlias.put( oja.getRhsAlias(), oja );
|
||||
positionsByAlias.put( oja.getRhsAlias(), position );
|
||||
EntityPersister entityPersister = null;
|
||||
if ( oja.getJoinableType().isCollectionType() ) {
|
||||
entityPersister = ( ( QueryableCollection) oja.getJoinable() ).getElementPersister();
|
||||
}
|
||||
else if ( oja.getJoinableType().isEntityType() ) {
|
||||
entityPersister = ( EntityPersister ) oja.getJoinable();
|
||||
}
|
||||
if ( entityPersister != null
|
||||
&& entityPersister.getIdentifierType().isComponentType()
|
||||
&& ! entityPersister.getEntityMetamodel().getIdentifierProperty().isEmbedded()
|
||||
&& hasAssociation( (ComponentType) entityPersister.getIdentifierType() ) ) {
|
||||
aliasesForAssociationsWithCompositesIds.add( oja.getRhsAlias() );
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasAssociation(ComponentType componentType) {
|
||||
int i = 0;
|
||||
for ( Type subType : componentType.getSubtypes() ) {
|
||||
if ( subType.isEntityType() ) {
|
||||
return true;
|
||||
}
|
||||
else if ( subType.isComponentType() && hasAssociation( ( (ComponentType) subType ) ) ) {
|
||||
return true;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int[][] resolve() {
|
||||
int[][] compositeKeyManyToOneTargetIndices = null;
|
||||
for ( final String aliasWithCompositeId : aliasesForAssociationsWithCompositesIds ) {
|
||||
final OuterJoinableAssociation joinWithCompositeId = associationsByAlias.get( aliasWithCompositeId );
|
||||
final ArrayList<Integer> keyManyToOneTargetIndices = new ArrayList<Integer>();
|
||||
// for each association with a composite id containing key-many-to-one(s), find the bidirectional side of
|
||||
// each key-many-to-one (if exists) to see if it is eager as well. If so, we need to track the indices
|
||||
EntityPersister entityPersister = null;
|
||||
if ( joinWithCompositeId.getJoinableType().isCollectionType() ) {
|
||||
entityPersister = ( ( QueryableCollection) joinWithCompositeId.getJoinable() ).getElementPersister();
|
||||
}
|
||||
else if ( joinWithCompositeId.getJoinableType().isEntityType() ) {
|
||||
entityPersister = ( EntityPersister ) joinWithCompositeId.getJoinable();
|
||||
}
|
||||
|
||||
findKeyManyToOneTargetIndices(
|
||||
keyManyToOneTargetIndices,
|
||||
joinWithCompositeId,
|
||||
(ComponentType) entityPersister.getIdentifierType()
|
||||
);
|
||||
|
||||
if ( ! keyManyToOneTargetIndices.isEmpty() ) {
|
||||
if ( compositeKeyManyToOneTargetIndices == null ) {
|
||||
compositeKeyManyToOneTargetIndices = new int[ associationsByAlias.size() ][];
|
||||
}
|
||||
int position = positionsByAlias.get( aliasWithCompositeId );
|
||||
compositeKeyManyToOneTargetIndices[position] = new int[ keyManyToOneTargetIndices.size() ];
|
||||
int i = 0;
|
||||
for ( int index : keyManyToOneTargetIndices ) {
|
||||
compositeKeyManyToOneTargetIndices[position][i] = index;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return compositeKeyManyToOneTargetIndices;
|
||||
}
|
||||
|
||||
private void findKeyManyToOneTargetIndices(
|
||||
ArrayList<Integer> keyManyToOneTargetIndices,
|
||||
OuterJoinableAssociation joinWithCompositeId,
|
||||
ComponentType componentType) {
|
||||
for ( Type subType : componentType.getSubtypes() ) {
|
||||
if ( subType.isEntityType() ) {
|
||||
Integer index = locateKeyManyToOneTargetIndex( joinWithCompositeId, (EntityType) subType );
|
||||
if ( index != null ) {
|
||||
keyManyToOneTargetIndices.add( index );
|
||||
}
|
||||
}
|
||||
else if ( subType.isComponentType() ) {
|
||||
findKeyManyToOneTargetIndices(
|
||||
keyManyToOneTargetIndices,
|
||||
joinWithCompositeId,
|
||||
(ComponentType) subType
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Integer locateKeyManyToOneTargetIndex(OuterJoinableAssociation joinWithCompositeId, EntityType keyManyToOneType) {
|
||||
// the lhs (if one) is a likely candidate
|
||||
if ( joinWithCompositeId.getLhsAlias() != null ) {
|
||||
final OuterJoinableAssociation lhs = associationsByAlias.get( joinWithCompositeId.getLhsAlias() );
|
||||
if ( keyManyToOneType.getAssociatedEntityName( factory ).equals( lhs.getJoinableType().getAssociatedEntityName( factory ) ) ) {
|
||||
return positionsByAlias.get( lhs.getRhsAlias() );
|
||||
}
|
||||
}
|
||||
// otherwise, seek out OuterJoinableAssociation which are RHS of given OuterJoinableAssociation
|
||||
// (joinWithCompositeId)
|
||||
for ( OuterJoinableAssociation oja : associationsByAlias.values() ) {
|
||||
if ( oja.getLhsAlias() != null && oja.getLhsAlias().equals( joinWithCompositeId.getRhsAlias() ) ) {
|
||||
if ( keyManyToOneType.equals( oja.getJoinableType() ) ) {
|
||||
return positionsByAlias.get( oja.getLhsAlias() );
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -24,14 +24,12 @@
|
|||
*/
|
||||
package org.hibernate.loader.entity;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.loader.JoinWalker;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
|
@ -46,6 +44,7 @@ import org.hibernate.type.Type;
|
|||
public class EntityLoader extends AbstractEntityLoader {
|
||||
|
||||
private final boolean batchLoader;
|
||||
private final int[][] compositeKeyManyToOneTargetIndices;
|
||||
|
||||
public EntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
|
@ -107,7 +106,7 @@ public class EntityLoader extends AbstractEntityLoader {
|
|||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
super( persister, uniqueKeyType, factory, loadQueryInfluencers );
|
||||
|
||||
JoinWalker walker = new EntityJoinWalker(
|
||||
EntityJoinWalker walker = new EntityJoinWalker(
|
||||
persister,
|
||||
uniqueKey,
|
||||
batchSize,
|
||||
|
@ -116,7 +115,7 @@ public class EntityLoader extends AbstractEntityLoader {
|
|||
loadQueryInfluencers
|
||||
);
|
||||
initFromWalker( walker );
|
||||
|
||||
this.compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
|
||||
postInstantiate();
|
||||
|
||||
batchLoader = batchSize > 1;
|
||||
|
@ -134,7 +133,7 @@ public class EntityLoader extends AbstractEntityLoader {
|
|||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
super( persister, uniqueKeyType, factory, loadQueryInfluencers );
|
||||
|
||||
JoinWalker walker = new EntityJoinWalker(
|
||||
EntityJoinWalker walker = new EntityJoinWalker(
|
||||
persister,
|
||||
uniqueKey,
|
||||
batchSize,
|
||||
|
@ -143,7 +142,7 @@ public class EntityLoader extends AbstractEntityLoader {
|
|||
loadQueryInfluencers
|
||||
);
|
||||
initFromWalker( walker );
|
||||
|
||||
this.compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
|
||||
postInstantiate();
|
||||
|
||||
batchLoader = batchSize > 1;
|
||||
|
@ -163,5 +162,8 @@ public class EntityLoader extends AbstractEntityLoader {
|
|||
protected boolean isSingleRowLoader() {
|
||||
return !batchLoader;
|
||||
}
|
||||
|
||||
|
||||
public int[][] getCompositeKeyManyToOneTargetIndices() {
|
||||
return compositeKeyManyToOneTargetIndices;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue