HHH-7841 - Introduce LoadPlan
This commit is contained in:
parent
d874bc4737
commit
af5e8c3869
|
@ -225,7 +225,8 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu
|
||||||
else {
|
else {
|
||||||
throw new NotYetImplementedException( "Cannot determine LHS alias for FetchOwner." );
|
throw new NotYetImplementedException( "Cannot determine LHS alias for FetchOwner." );
|
||||||
}
|
}
|
||||||
final String[] aliasedLhsColumnNames = StringHelper.qualify( lhsAlias, currentFetch.getColumnNames() );
|
|
||||||
|
final String[] aliasedLhsColumnNames = currentFetch.toSqlSelectFragments( lhsAlias );
|
||||||
final String rhsAlias;
|
final String rhsAlias;
|
||||||
if ( EntityReference.class.isInstance( currentFetch ) ) {
|
if ( EntityReference.class.isInstance( currentFetch ) ) {
|
||||||
rhsAlias = resolveEntityTableAlias( (EntityReference) currentFetch );
|
rhsAlias = resolveEntityTableAlias( (EntityReference) currentFetch );
|
||||||
|
|
|
@ -25,7 +25,6 @@ package org.hibernate.loader.plan.internal;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.engine.FetchStrategy;
|
import org.hibernate.engine.FetchStrategy;
|
||||||
import org.hibernate.loader.plan.spi.AbstractFetchOwner;
|
|
||||||
import org.hibernate.loader.plan.spi.CollectionFetch;
|
import org.hibernate.loader.plan.spi.CollectionFetch;
|
||||||
import org.hibernate.loader.plan.spi.CompositeFetch;
|
import org.hibernate.loader.plan.spi.CompositeFetch;
|
||||||
import org.hibernate.loader.plan.spi.EntityFetch;
|
import org.hibernate.loader.plan.spi.EntityFetch;
|
||||||
|
@ -33,7 +32,6 @@ import org.hibernate.loader.plan.spi.FetchOwner;
|
||||||
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
|
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
|
||||||
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
||||||
import org.hibernate.persister.walking.spi.CompositionDefinition;
|
import org.hibernate.persister.walking.spi.CompositionDefinition;
|
||||||
import org.hibernate.type.EntityType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
|
|
@ -108,17 +108,17 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isNullable(Fetch fetch) {
|
public boolean isNullable(Fetch fetch) {
|
||||||
return getFetchOwnerDelegate().isNullable( fetch );
|
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).isNullable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type getType(Fetch fetch) {
|
public Type getType(Fetch fetch) {
|
||||||
return getFetchOwnerDelegate().getType( fetch );
|
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getColumnNames(Fetch fetch) {
|
public String[] toSqlSelectFragments(Fetch fetch, String alias) {
|
||||||
return getFetchOwnerDelegate().getColumnNames( fetch );
|
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).toSqlSelectFragments( alias );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013, 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.plan.spi;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base implementation of FetchOwnerDelegate providing caching of FetchMetadata.
|
||||||
|
*
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public abstract class AbstractFetchOwnerDelegate implements FetchOwnerDelegate {
|
||||||
|
private Map<String,FetchMetadata> fetchMetadataMap;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FetchMetadata locateFetchMetadata(Fetch fetch) {
|
||||||
|
FetchMetadata metadata = fetchMetadataMap == null ? null : fetchMetadataMap.get( fetch.getOwnerPropertyName() );
|
||||||
|
if ( metadata == null ) {
|
||||||
|
if ( fetchMetadataMap == null ) {
|
||||||
|
fetchMetadataMap = new HashMap<String, FetchMetadata>();
|
||||||
|
}
|
||||||
|
metadata = buildFetchMetadata( fetch );
|
||||||
|
fetchMetadataMap.put( fetch.getOwnerPropertyName(), metadata );
|
||||||
|
}
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract FetchMetadata buildFetchMetadata(Fetch fetch);
|
||||||
|
}
|
|
@ -92,8 +92,8 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getColumnNames() {
|
public String[] toSqlSelectFragments(String alias) {
|
||||||
return owner.getColumnNames( this );
|
return owner.toSqlSelectFragments( this, alias );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -28,11 +28,8 @@ import java.sql.SQLException;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.engine.FetchStrategy;
|
import org.hibernate.engine.FetchStrategy;
|
||||||
import org.hibernate.engine.internal.JoinHelper;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.loader.spi.ResultSetProcessingContext;
|
import org.hibernate.loader.spi.ResultSetProcessingContext;
|
||||||
import org.hibernate.persister.entity.Joinable;
|
|
||||||
import org.hibernate.type.CollectionType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
@ -82,8 +79,8 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getColumnNames() {
|
public String[] toSqlSelectFragments(String alias) {
|
||||||
return getOwner().getColumnNames( this );
|
return getOwner().toSqlSelectFragments( this, alias );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -42,7 +42,12 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
|
||||||
this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate(
|
this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate(
|
||||||
sessionFactory,
|
sessionFactory,
|
||||||
(CompositeType) collectionPersister.getElementType(),
|
(CompositeType) collectionPersister.getElementType(),
|
||||||
( (QueryableCollection) collectionPersister ).getElementColumnNames()
|
new CompositeFetchOwnerDelegate.PropertyMappingDelegate() {
|
||||||
|
@Override
|
||||||
|
public String[] toSqlSelectFragments(String alias) {
|
||||||
|
return ( (QueryableCollection) collectionPersister ).getElementColumnNames( alias );
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ import org.hibernate.type.CompositeType;
|
||||||
* @author Gail Badner
|
* @author Gail Badner
|
||||||
*/
|
*/
|
||||||
public class CompositeFetch extends AbstractSingularAttributeFetch {
|
public class CompositeFetch extends AbstractSingularAttributeFetch {
|
||||||
public static final FetchStrategy FETCH_PLAN = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN );
|
private static final FetchStrategy FETCH_PLAN = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN );
|
||||||
|
|
||||||
private final FetchOwnerDelegate delegate;
|
private final FetchOwnerDelegate delegate;
|
||||||
|
|
||||||
|
@ -54,13 +54,18 @@ public class CompositeFetch extends AbstractSingularAttributeFetch {
|
||||||
*/
|
*/
|
||||||
public CompositeFetch(
|
public CompositeFetch(
|
||||||
SessionFactoryImplementor sessionFactory,
|
SessionFactoryImplementor sessionFactory,
|
||||||
FetchOwner owner,
|
final FetchOwner owner,
|
||||||
String ownerProperty) {
|
String ownerProperty) {
|
||||||
super( sessionFactory, owner, ownerProperty, FETCH_PLAN );
|
super( sessionFactory, owner, ownerProperty, FETCH_PLAN );
|
||||||
this.delegate = new CompositeFetchOwnerDelegate(
|
this.delegate = new CompositeFetchOwnerDelegate(
|
||||||
sessionFactory,
|
sessionFactory,
|
||||||
(CompositeType) getOwner().getType( this ),
|
(CompositeType) getOwner().getType( this ),
|
||||||
getOwner().getColumnNames( this )
|
new CompositeFetchOwnerDelegate.PropertyMappingDelegate() {
|
||||||
|
@Override
|
||||||
|
public String[] toSqlSelectFragments(String alias) {
|
||||||
|
return owner.toSqlSelectFragments( CompositeFetch.this, alias );
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ package org.hibernate.loader.plan.spi;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
|
||||||
import org.hibernate.persister.walking.spi.WalkingException;
|
import org.hibernate.persister.walking.spi.WalkingException;
|
||||||
import org.hibernate.type.CompositeType;
|
import org.hibernate.type.CompositeType;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
@ -36,72 +35,109 @@ import org.hibernate.type.Type;
|
||||||
* owned sub-attribute fetch.
|
* owned sub-attribute fetch.
|
||||||
*
|
*
|
||||||
* @author Gail Badner
|
* @author Gail Badner
|
||||||
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class CompositeFetchOwnerDelegate implements FetchOwnerDelegate {
|
public class CompositeFetchOwnerDelegate extends AbstractFetchOwnerDelegate implements FetchOwnerDelegate {
|
||||||
private final SessionFactoryImplementor sessionFactory;
|
private final SessionFactoryImplementor sessionFactory;
|
||||||
private final CompositeType compositeType;
|
private final CompositeType compositeType;
|
||||||
private final String[] columnNames;
|
private final PropertyMappingDelegate propertyMappingDelegate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a {@link CompositeFetchOwnerDelegate}.
|
* Constructs a CompositeFetchOwnerDelegate
|
||||||
|
*
|
||||||
* @param sessionFactory - the session factory.
|
* @param sessionFactory - the session factory.
|
||||||
* @param compositeType - the composite type.
|
* @param compositeType - the composite type.
|
||||||
* @param columnNames - the column names used by sub-attribute fetches.
|
* @param propertyMappingDelegate - delegate for handling property mapping
|
||||||
*/
|
*/
|
||||||
public CompositeFetchOwnerDelegate(
|
public CompositeFetchOwnerDelegate(
|
||||||
SessionFactoryImplementor sessionFactory,
|
SessionFactoryImplementor sessionFactory,
|
||||||
CompositeType compositeType,
|
CompositeType compositeType,
|
||||||
String[] columnNames) {
|
PropertyMappingDelegate propertyMappingDelegate) {
|
||||||
this.sessionFactory = sessionFactory;
|
this.sessionFactory = sessionFactory;
|
||||||
this.compositeType = compositeType;
|
this.compositeType = compositeType;
|
||||||
this.columnNames = columnNames;
|
this.propertyMappingDelegate = propertyMappingDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface PropertyMappingDelegate {
|
||||||
|
public String[] toSqlSelectFragments(String alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isNullable(Fetch fetch) {
|
protected FetchMetadata buildFetchMetadata(Fetch fetch) {
|
||||||
return compositeType.getPropertyNullability()[ determinePropertyIndex( fetch ) ];
|
int subIndex = -1;
|
||||||
}
|
int selectFragmentRangeStart = 0;
|
||||||
|
int selectFragmentRangeEnd = -1;
|
||||||
|
|
||||||
@Override
|
for ( int i = 0; i < compositeType.getPropertyNames().length; i++ ) {
|
||||||
public Type getType(Fetch fetch) {
|
final Type type = compositeType.getSubtypes()[i];
|
||||||
return compositeType.getSubtypes()[ determinePropertyIndex( fetch ) ];
|
final int typeColSpan = type.getColumnSpan( sessionFactory );
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getColumnNames(Fetch fetch) {
|
|
||||||
// TODO: probably want to cache this
|
|
||||||
int begin = 0;
|
|
||||||
String[] subColumnNames = null;
|
|
||||||
for ( int i = 0; i < compositeType.getSubtypes().length; i++ ) {
|
|
||||||
final int columnSpan = compositeType.getSubtypes()[i].getColumnSpan( sessionFactory );
|
|
||||||
subColumnNames = ArrayHelper.slice( columnNames, begin, columnSpan );
|
|
||||||
if ( compositeType.getPropertyNames()[ i ].equals( fetch.getOwnerPropertyName() ) ) {
|
if ( compositeType.getPropertyNames()[ i ].equals( fetch.getOwnerPropertyName() ) ) {
|
||||||
|
// fount it!
|
||||||
|
subIndex = i;
|
||||||
|
selectFragmentRangeEnd = selectFragmentRangeStart + typeColSpan;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
begin += columnSpan;
|
selectFragmentRangeStart += typeColSpan;
|
||||||
}
|
|
||||||
return subColumnNames;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int determinePropertyIndex(Fetch fetch) {
|
if ( subIndex < 0 ) {
|
||||||
// TODO: probably want to cache this
|
|
||||||
final String[] subAttributeNames = compositeType.getPropertyNames();
|
|
||||||
int subAttributeIndex = -1;
|
|
||||||
for ( int i = 0; i < subAttributeNames.length ; i++ ) {
|
|
||||||
if ( subAttributeNames[ i ].equals( fetch.getOwnerPropertyName() ) ) {
|
|
||||||
subAttributeIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( subAttributeIndex == -1 ) {
|
|
||||||
throw new WalkingException(
|
throw new WalkingException(
|
||||||
String.format(
|
String.format(
|
||||||
"Owner property [%s] not found in composite properties [%s]",
|
"Owner property [%s] not found in composite properties [%s]",
|
||||||
fetch.getOwnerPropertyName(),
|
fetch.getOwnerPropertyName(),
|
||||||
Arrays.asList( subAttributeNames )
|
Arrays.asList( compositeType.getPropertyNames() )
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return subAttributeIndex;
|
|
||||||
|
return new FetchMetadataImpl(
|
||||||
|
compositeType,
|
||||||
|
subIndex,
|
||||||
|
propertyMappingDelegate,
|
||||||
|
selectFragmentRangeStart,
|
||||||
|
selectFragmentRangeEnd
|
||||||
|
);
|
||||||
|
|
||||||
|
// todo : we really need a PropertyMapping delegate which can encapsulate both the PropertyMapping and the path
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FetchMetadataImpl implements FetchMetadata {
|
||||||
|
private final CompositeType compositeType;
|
||||||
|
private final int index;
|
||||||
|
private final PropertyMappingDelegate propertyMappingDelegate;
|
||||||
|
private final int selectFragmentRangeStart;
|
||||||
|
private final int selectFragmentRangeEnd;
|
||||||
|
|
||||||
|
public FetchMetadataImpl(
|
||||||
|
CompositeType compositeType,
|
||||||
|
int index,
|
||||||
|
PropertyMappingDelegate propertyMappingDelegate,
|
||||||
|
int selectFragmentRangeStart,
|
||||||
|
int selectFragmentRangeEnd) {
|
||||||
|
this.compositeType = compositeType;
|
||||||
|
this.index = index;
|
||||||
|
this.propertyMappingDelegate = propertyMappingDelegate;
|
||||||
|
this.selectFragmentRangeStart = selectFragmentRangeStart;
|
||||||
|
this.selectFragmentRangeEnd = selectFragmentRangeEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNullable() {
|
||||||
|
return compositeType.getPropertyNullability()[ index ];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return compositeType.getSubtypes()[ index ];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] toSqlSelectFragments(String alias) {
|
||||||
|
return Arrays.copyOfRange(
|
||||||
|
propertyMappingDelegate.toSqlSelectFragments( alias ),
|
||||||
|
selectFragmentRangeStart,
|
||||||
|
selectFragmentRangeEnd
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,12 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable
|
||||||
this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate(
|
this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate(
|
||||||
sessionFactory,
|
sessionFactory,
|
||||||
(CompositeType) collectionPersister.getIndexType(),
|
(CompositeType) collectionPersister.getIndexType(),
|
||||||
( (QueryableCollection) collectionPersister ).getIndexColumnNames()
|
new CompositeFetchOwnerDelegate.PropertyMappingDelegate() {
|
||||||
|
@Override
|
||||||
|
public String[] toSqlSelectFragments(String alias) {
|
||||||
|
return ( (QueryableCollection) collectionPersister ).getIndexColumnNames( alias );
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,14 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.plan.spi;
|
package org.hibernate.loader.plan.spi;
|
||||||
|
|
||||||
import org.hibernate.engine.internal.JoinHelper;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
import org.hibernate.persister.entity.Queryable;
|
||||||
import org.hibernate.type.AssociationType;
|
import org.hibernate.persister.walking.spi.WalkingException;
|
||||||
|
import org.hibernate.type.CompositeType;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,8 +38,9 @@ import org.hibernate.type.Type;
|
||||||
* an owned attribute fetch.
|
* an owned attribute fetch.
|
||||||
*
|
*
|
||||||
* @author Gail Badner
|
* @author Gail Badner
|
||||||
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class EntityFetchOwnerDelegate implements FetchOwnerDelegate {
|
public class EntityFetchOwnerDelegate extends AbstractFetchOwnerDelegate implements FetchOwnerDelegate {
|
||||||
private final EntityPersister entityPersister;
|
private final EntityPersister entityPersister;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,35 +53,149 @@ public class EntityFetchOwnerDelegate implements FetchOwnerDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isNullable(Fetch fetch) {
|
protected FetchMetadata buildFetchMetadata(Fetch fetch) {
|
||||||
return entityPersister.getPropertyNullability()[ determinePropertyIndex( fetch ) ];
|
final Integer propertyIndex = entityPersister.getEntityMetamodel().getPropertyIndexOrNull(
|
||||||
}
|
fetch.getOwnerPropertyName()
|
||||||
|
);
|
||||||
@Override
|
if ( propertyIndex == null ) {
|
||||||
public Type getType(Fetch fetch) {
|
// possibly it is part of the identifier; but that's only possible if the identifier is composite
|
||||||
return entityPersister.getPropertyTypes()[ determinePropertyIndex( fetch ) ];
|
final Type idType = entityPersister.getIdentifierType();
|
||||||
}
|
if ( CompositeType.class.isInstance( idType ) ) {
|
||||||
|
final CompositeType cidType = (CompositeType) idType;
|
||||||
@Override
|
if ( entityPersister.hasIdentifierProperty() ) {
|
||||||
public String[] getColumnNames(Fetch fetch) {
|
// encapsulated composite id case; this *should have* been handled as part of building the fetch...
|
||||||
// TODO: cache this info
|
throw new WalkingException(
|
||||||
final OuterJoinLoadable outerJoinLoadable = (OuterJoinLoadable) entityPersister;
|
"Expecting different FetchOwnerDelegate type for encapsulated composite id case"
|
||||||
Type fetchType = getType( fetch );
|
|
||||||
if ( fetchType.isAssociationType() ) {
|
|
||||||
return JoinHelper.getLHSColumnNames(
|
|
||||||
(AssociationType) fetchType,
|
|
||||||
determinePropertyIndex( fetch ),
|
|
||||||
outerJoinLoadable,
|
|
||||||
outerJoinLoadable.getFactory()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return outerJoinLoadable.getPropertyColumnNames( determinePropertyIndex( fetch ) );
|
// non-encapsulated composite id case...
|
||||||
|
return new NonEncapsulatedIdentifierAttributeFetchMetadata(
|
||||||
|
entityPersister,
|
||||||
|
cidType,
|
||||||
|
fetch.getOwnerPropertyName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new NonIdentifierAttributeFetchMetadata(
|
||||||
|
entityPersister,
|
||||||
|
fetch.getOwnerPropertyName(),
|
||||||
|
propertyIndex.intValue()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new WalkingException(
|
||||||
|
"Could not locate metadata about given fetch [" + fetch + "] in its owning persister"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NonIdentifierAttributeFetchMetadata implements FetchMetadata {
|
||||||
|
private final EntityPersister entityPersister;
|
||||||
|
private final String attributeName;
|
||||||
|
private final int propertyIndex;
|
||||||
|
|
||||||
|
private Type attributeType;
|
||||||
|
|
||||||
|
public NonIdentifierAttributeFetchMetadata(
|
||||||
|
EntityPersister entityPersister,
|
||||||
|
String attributeName,
|
||||||
|
int propertyIndex) {
|
||||||
|
this.entityPersister = entityPersister;
|
||||||
|
this.attributeName = attributeName;
|
||||||
|
this.propertyIndex = propertyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNullable() {
|
||||||
|
return entityPersister.getPropertyNullability()[ propertyIndex ];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
if ( attributeType == null ) {
|
||||||
|
attributeType = entityPersister.getPropertyTypes()[ propertyIndex ];
|
||||||
|
}
|
||||||
|
return attributeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] toSqlSelectFragments(String alias) {
|
||||||
|
// final Type type = getType();
|
||||||
|
// final OuterJoinLoadable outerJoinLoadable = (OuterJoinLoadable) entityPersister;
|
||||||
|
final Queryable queryable = (Queryable) entityPersister;
|
||||||
|
|
||||||
|
return queryable.toColumns( alias, attributeName );
|
||||||
|
// if ( type.isAssociationType() ) {
|
||||||
|
// return JoinHelper.getLHSColumnNames(
|
||||||
|
// (AssociationType) type,
|
||||||
|
// propertyIndex,
|
||||||
|
// outerJoinLoadable,
|
||||||
|
// outerJoinLoadable.getFactory()
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// return outerJoinLoadable.getPropertyColumnNames( propertyIndex );
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int determinePropertyIndex(Fetch fetch) {
|
private class NonEncapsulatedIdentifierAttributeFetchMetadata implements FetchMetadata {
|
||||||
// TODO: cache this info
|
private final EntityPersister entityPersister;
|
||||||
return entityPersister.getEntityMetamodel().getPropertyIndex( fetch.getOwnerPropertyName() );
|
private final String attributeName;
|
||||||
|
|
||||||
|
// virtually final fields
|
||||||
|
private Type type;
|
||||||
|
private int selectFragmentRangeStart;
|
||||||
|
private int selectFragmentRangeEnd;
|
||||||
|
|
||||||
|
|
||||||
|
public NonEncapsulatedIdentifierAttributeFetchMetadata(
|
||||||
|
EntityPersister entityPersister,
|
||||||
|
CompositeType cidType,
|
||||||
|
String attributeName) {
|
||||||
|
this.entityPersister = entityPersister;
|
||||||
|
this.attributeName = attributeName;
|
||||||
|
|
||||||
|
this.selectFragmentRangeStart = 0;
|
||||||
|
Type subType;
|
||||||
|
boolean foundIt = false;
|
||||||
|
for ( int i = 0; i < cidType.getPropertyNames().length; i++ ) {
|
||||||
|
subType = cidType.getSubtypes()[i];
|
||||||
|
if ( cidType.getPropertyNames()[i].equals( attributeName ) ) {
|
||||||
|
// found it!
|
||||||
|
foundIt = true;
|
||||||
|
this.type = subType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
selectFragmentRangeStart += subType.getColumnSpan( entityPersister.getFactory() );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !foundIt ) {
|
||||||
|
throw new WalkingException( "Could not find " );
|
||||||
|
}
|
||||||
|
|
||||||
|
selectFragmentRangeEnd = selectFragmentRangeStart + type.getColumnSpan( entityPersister.getFactory() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNullable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] toSqlSelectFragments(String alias) {
|
||||||
|
return Arrays.copyOfRange(
|
||||||
|
( (Queryable) entityPersister ).toColumns( alias, attributeName ),
|
||||||
|
selectFragmentRangeStart,
|
||||||
|
selectFragmentRangeEnd
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,11 +60,11 @@ public interface Fetch extends CopyableFetch {
|
||||||
public boolean isNullable();
|
public boolean isNullable();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the column names used for this fetch.
|
* Generates the SQL select fragments for this fetch. A select fragment is the column and formula references.
|
||||||
*
|
*
|
||||||
* @return the column names used for this fetch.
|
* @return the select fragments
|
||||||
*/
|
*/
|
||||||
public String[] getColumnNames();
|
public String[] toSqlSelectFragments(String alias);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the fetch strategy for this fetch.
|
* Gets the fetch strategy for this fetch.
|
||||||
|
|
|
@ -76,13 +76,15 @@ public interface FetchOwner {
|
||||||
public boolean isNullable(Fetch fetch);
|
public boolean isNullable(Fetch fetch);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the column names used for loading the specified fetch.
|
* Generates the SQL select fragments for the specified fetch. A select fragment is the column and formula
|
||||||
|
* references.
|
||||||
*
|
*
|
||||||
* @param fetch - the owned fetch.
|
* @param fetch - the owned fetch.
|
||||||
|
* @param alias The table alias to apply to the fragments (used to qualify column references)
|
||||||
*
|
*
|
||||||
* @return the column names used for loading the specified fetch.
|
* @return the select fragments
|
||||||
*/
|
*/
|
||||||
public String[] getColumnNames(Fetch fetch);
|
public String[] toSqlSelectFragments(Fetch fetch, String alias);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the asserted plan valid from this owner to a fetch?
|
* Is the asserted plan valid from this owner to a fetch?
|
||||||
|
|
|
@ -31,31 +31,39 @@ import org.hibernate.type.Type;
|
||||||
* @author Gail Badner
|
* @author Gail Badner
|
||||||
*/
|
*/
|
||||||
public interface FetchOwnerDelegate {
|
public interface FetchOwnerDelegate {
|
||||||
|
public static interface FetchMetadata {
|
||||||
/**
|
/**
|
||||||
* Is the specified fetch nullable?
|
* Is the fetch nullable?
|
||||||
*
|
|
||||||
* @param fetch - the owned fetch.
|
|
||||||
*
|
*
|
||||||
* @return true, if the fetch is nullable; false, otherwise.
|
* @return true, if the fetch is nullable; false, otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isNullable(Fetch fetch);
|
public boolean isNullable();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the type of the specified fetch.
|
* Returns the type of the fetched attribute
|
||||||
*
|
*
|
||||||
* @param fetch - the owned fetch.
|
* @return the type of the fetched attribute.
|
||||||
*
|
|
||||||
* @return the type of the specified fetch.
|
|
||||||
*/
|
*/
|
||||||
public Type getType(Fetch fetch);
|
public Type getType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the column names used for loading the specified fetch.
|
* Generates the SQL select fragments for the specified fetch. A select fragment is the column and formula
|
||||||
|
* references.
|
||||||
*
|
*
|
||||||
* @param fetch - the owned fetch.
|
* @param alias The table alias to apply to the fragments (used to qualify column references)
|
||||||
*
|
*
|
||||||
* @return the column names used for loading the specified fetch.
|
* @return the select fragments
|
||||||
*/
|
*/
|
||||||
public String[] getColumnNames(Fetch fetch);
|
public String[] toSqlSelectFragments(String alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locate the metadata for the specified Fetch. Allows easier caching of the resolved information.
|
||||||
|
*
|
||||||
|
* @param fetch The fetch for which to locate metadata
|
||||||
|
*
|
||||||
|
* @return The metadata; never {@code null}, rather an exception is thrown if the information for the fetch cannot
|
||||||
|
* be located.
|
||||||
|
*/
|
||||||
|
public FetchMetadata locateFetchMetadata(Fetch fetch);
|
||||||
}
|
}
|
|
@ -43,6 +43,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.loader.PropertyPath;
|
import org.hibernate.loader.PropertyPath;
|
||||||
import org.hibernate.loader.plan.spi.AbstractFetchOwner;
|
import org.hibernate.loader.plan.spi.AbstractFetchOwner;
|
||||||
|
import org.hibernate.loader.plan.spi.AbstractFetchOwnerDelegate;
|
||||||
import org.hibernate.loader.plan.spi.CollectionFetch;
|
import org.hibernate.loader.plan.spi.CollectionFetch;
|
||||||
import org.hibernate.loader.plan.spi.CollectionReference;
|
import org.hibernate.loader.plan.spi.CollectionReference;
|
||||||
import org.hibernate.loader.plan.spi.CollectionReturn;
|
import org.hibernate.loader.plan.spi.CollectionReturn;
|
||||||
|
@ -573,16 +574,17 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type getType(Fetch fetch) {
|
public Type getType(Fetch fetch) {
|
||||||
if ( !fetch.getOwnerPropertyName().equals( entityReference.getEntityPersister().getIdentifierPropertyName() ) ) {
|
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).getType();
|
||||||
throw new IllegalArgumentException(
|
|
||||||
String.format(
|
|
||||||
"Fetch owner property name [%s] is not the same as the identifier property name [%s].",
|
|
||||||
fetch.getOwnerPropertyName(),
|
|
||||||
entityReference.getEntityPersister().getIdentifierPropertyName()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return super.getType( fetch );
|
|
||||||
|
@Override
|
||||||
|
public boolean isNullable(Fetch fetch) {
|
||||||
|
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).isNullable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] toSqlSelectFragments(Fetch fetch, String alias) {
|
||||||
|
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).toSqlSelectFragments( alias );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -620,21 +622,15 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
final EntityReference entityReference) {
|
final EntityReference entityReference) {
|
||||||
super( sessionFactory, entityReference );
|
super( sessionFactory, entityReference );
|
||||||
this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath();
|
this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath();
|
||||||
|
this.delegate = new AbstractFetchOwnerDelegate() {
|
||||||
final boolean isCompositeType = entityReference.getEntityPersister().getIdentifierType().isComponentType();
|
final boolean isCompositeType = entityReference.getEntityPersister().getIdentifierType().isComponentType();
|
||||||
this.delegate = new FetchOwnerDelegate() {
|
|
||||||
@Override
|
|
||||||
public boolean isNullable(Fetch fetch) {
|
|
||||||
if ( !isCompositeType ) {
|
|
||||||
throw new IllegalStateException( "Non-composite ID cannot have fetches." );
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type getType(Fetch fetch) {
|
protected FetchMetadata buildFetchMetadata(Fetch fetch) {
|
||||||
if ( !isCompositeType ) {
|
if ( !isCompositeType ) {
|
||||||
throw new IllegalStateException( "Non-composite ID cannot have fetches." );
|
throw new WalkingException( "Non-composite identifier cannot be a fetch owner" );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !fetch.getOwnerPropertyName().equals( entityReference.getEntityPersister().getIdentifierPropertyName() ) ) {
|
if ( !fetch.getOwnerPropertyName().equals( entityReference.getEntityPersister().getIdentifierPropertyName() ) ) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
String.format(
|
String.format(
|
||||||
|
@ -644,15 +640,24 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new FetchMetadata() {
|
||||||
|
@Override
|
||||||
|
public boolean isNullable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
return entityReference.getEntityPersister().getIdentifierType();
|
return entityReference.getEntityPersister().getIdentifierType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getColumnNames(Fetch fetch) {
|
public String[] toSqlSelectFragments(String alias) {
|
||||||
if ( !isCompositeType ) {
|
// should not ever be called iiuc...
|
||||||
throw new IllegalStateException( "Non-composite ID cannot have fetches." );
|
throw new WalkingException( "Should not be called" );
|
||||||
}
|
}
|
||||||
return ( (Loadable) entityReference.getEntityPersister() ).getIdentifierColumnNames();
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -681,16 +686,65 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
private final PropertyPath propertyPath;
|
private final PropertyPath propertyPath;
|
||||||
private final FetchOwnerDelegate fetchOwnerDelegate;
|
private final FetchOwnerDelegate fetchOwnerDelegate;
|
||||||
|
|
||||||
public NonEncapsulatedIdentifierAttributeCollector(SessionFactoryImplementor sessionfactory, EntityReference entityReference) {
|
public NonEncapsulatedIdentifierAttributeCollector(
|
||||||
|
final SessionFactoryImplementor sessionfactory,
|
||||||
|
final EntityReference entityReference) {
|
||||||
super( sessionfactory, entityReference );
|
super( sessionfactory, entityReference );
|
||||||
this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "<id>" );
|
this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "<id>" );
|
||||||
this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate(
|
this.fetchOwnerDelegate = new AbstractFetchOwnerDelegate() {
|
||||||
entityReference.getEntityPersister().getFactory(),
|
final boolean isCompositeType = entityReference.getEntityPersister().getIdentifierType().isComponentType();
|
||||||
(CompositeType) entityReference.getEntityPersister().getIdentifierType(),
|
final CompositeType idType = (CompositeType) entityReference.getEntityPersister().getIdentifierType();
|
||||||
( (Loadable) entityReference.getEntityPersister() ).getIdentifierColumnNames()
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FetchMetadata buildFetchMetadata(Fetch fetch) {
|
||||||
|
if ( !isCompositeType ) {
|
||||||
|
throw new WalkingException( "Non-composite identifier cannot be a fetch owner" );
|
||||||
|
}
|
||||||
|
|
||||||
|
final int subPropertyIndex = locateSubPropertyIndex( idType, fetch.getOwnerPropertyName() );
|
||||||
|
|
||||||
|
return new FetchMetadata() {
|
||||||
|
final Type subType = idType.getSubtypes()[ subPropertyIndex ];
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNullable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return subType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] toSqlSelectFragments(String alias) {
|
||||||
|
// should not ever be called iiuc...
|
||||||
|
throw new WalkingException( "Should not be called" );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private int locateSubPropertyIndex(CompositeType idType, String ownerPropertyName) {
|
||||||
|
for ( int i = 0; i < idType.getPropertyNames().length; i++ ) {
|
||||||
|
if ( ownerPropertyName.equals( idType.getPropertyNames()[i] ) ) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// does not bode well if we get here...
|
||||||
|
throw new IllegalStateException(
|
||||||
|
String.format(
|
||||||
|
"Unable to locate fetched attribute [%s] as part of composite identifier [%s]",
|
||||||
|
ownerPropertyName,
|
||||||
|
getPropertyPath().getFullPath()
|
||||||
|
)
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected IdentifierDescription buildIdentifierDescription() {
|
protected IdentifierDescription buildIdentifierDescription() {
|
||||||
return new IdentifierDescriptionImpl(
|
return new IdentifierDescriptionImpl(
|
||||||
|
@ -710,6 +764,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
protected FetchOwnerDelegate getFetchOwnerDelegate() {
|
protected FetchOwnerDelegate getFetchOwnerDelegate() {
|
||||||
return fetchOwnerDelegate;
|
return fetchOwnerDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class IdentifierDescriptionImpl implements IdentifierDescription {
|
private static class IdentifierDescriptionImpl implements IdentifierDescription {
|
||||||
|
|
|
@ -0,0 +1,232 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013, 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 java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.LockOptions;
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.Transaction;
|
||||||
|
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||||
|
import org.hibernate.engine.spi.QueryParameters;
|
||||||
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
|
import org.hibernate.jdbc.Work;
|
||||||
|
import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl;
|
||||||
|
import org.hibernate.loader.internal.LoadQueryAliasResolutionContextImpl;
|
||||||
|
import org.hibernate.loader.internal.ResultSetProcessorImpl;
|
||||||
|
import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy;
|
||||||
|
import org.hibernate.loader.plan.spi.LoadPlan;
|
||||||
|
import org.hibernate.loader.plan.spi.build.LoadPlanBuilder;
|
||||||
|
import org.hibernate.loader.spi.LoadQueryAliasResolutionContext;
|
||||||
|
import org.hibernate.loader.spi.NamedParameterContext;
|
||||||
|
import org.hibernate.loader.spi.NoOpLoadPlanAdvisor;
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
import org.hibernate.test.onetoone.formula.Address;
|
||||||
|
import org.hibernate.test.onetoone.formula.Person;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class NonEncapsulatedCompositeIdResultSetProcessorTest extends BaseCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String[] getMappings() {
|
||||||
|
return new String[] { "onetoone/formula/Person.hbm.xml" };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCompositeIdWithKeyManyToOne() throws Exception {
|
||||||
|
final String personId = "John Doe";
|
||||||
|
|
||||||
|
Person p = new Person();
|
||||||
|
p.setName( personId );
|
||||||
|
final Address a = new Address();
|
||||||
|
a.setPerson( p );
|
||||||
|
p.setAddress( a );
|
||||||
|
a.setType( "HOME" );
|
||||||
|
a.setStreet( "Main St" );
|
||||||
|
a.setState( "Sweet Home Alabama" );
|
||||||
|
a.setZip( "3181" );
|
||||||
|
|
||||||
|
Session s = openSession();
|
||||||
|
Transaction t = s.beginTransaction();
|
||||||
|
s.persist( p );
|
||||||
|
t.commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
final EntityPersister personPersister = sessionFactory().getEntityPersister( Person.class.getName() );
|
||||||
|
final EntityPersister addressPersister = sessionFactory().getEntityPersister( Address.class.getName() );
|
||||||
|
|
||||||
|
{
|
||||||
|
final List results = getResults(
|
||||||
|
addressPersister,
|
||||||
|
new Callback() {
|
||||||
|
@Override
|
||||||
|
public void bind(PreparedStatement ps) throws SQLException {
|
||||||
|
ps.setString( 1, personId );
|
||||||
|
ps.setString( 2, "HOME" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QueryParameters getQueryParameters() {
|
||||||
|
QueryParameters qp = new QueryParameters();
|
||||||
|
qp.setPositionalParameterTypes( new Type[] { addressPersister.getIdentifierType() } );
|
||||||
|
qp.setPositionalParameterValues( new Object[] { a } );
|
||||||
|
qp.setOptionalObject( a );
|
||||||
|
qp.setOptionalEntityName( addressPersister.getEntityName() );
|
||||||
|
qp.setOptionalId( a );
|
||||||
|
qp.setLockOptions( LockOptions.NONE );
|
||||||
|
return qp;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assertEquals( 1, results.size() );
|
||||||
|
Object result = results.get( 0 );
|
||||||
|
assertNotNull( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
// test loading the Person (the entity with normal id def, but mixed composite fk to Address)
|
||||||
|
{
|
||||||
|
final List results = getResults(
|
||||||
|
personPersister,
|
||||||
|
new Callback() {
|
||||||
|
@Override
|
||||||
|
public void bind(PreparedStatement ps) throws SQLException {
|
||||||
|
ps.setString( 1, personId );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QueryParameters getQueryParameters() {
|
||||||
|
QueryParameters qp = new QueryParameters();
|
||||||
|
qp.setPositionalParameterTypes( new Type[] { personPersister.getIdentifierType() } );
|
||||||
|
qp.setPositionalParameterValues( new Object[] { personId } );
|
||||||
|
qp.setOptionalObject( null );
|
||||||
|
qp.setOptionalEntityName( personPersister.getEntityName() );
|
||||||
|
qp.setOptionalId( personId );
|
||||||
|
qp.setLockOptions( LockOptions.NONE );
|
||||||
|
return qp;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assertEquals( 1, results.size() );
|
||||||
|
Object result = results.get( 0 );
|
||||||
|
assertNotNull( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
// CardField cardFieldWork = ExtraAssertions.assertTyping( CardField.class, result );
|
||||||
|
// assertEquals( cardFieldGotten, cardFieldWork );
|
||||||
|
|
||||||
|
// clean up test data
|
||||||
|
s = openSession();
|
||||||
|
s.beginTransaction();
|
||||||
|
s.createQuery( "delete Address" ).executeUpdate();
|
||||||
|
s.createQuery( "delete Person" ).executeUpdate();
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List getResults(final EntityPersister entityPersister, final Callback callback) {
|
||||||
|
final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy(
|
||||||
|
sessionFactory(),
|
||||||
|
LoadQueryInfluencers.NONE
|
||||||
|
);
|
||||||
|
final LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
|
||||||
|
final LoadQueryAliasResolutionContext aliasResolutionContext =
|
||||||
|
new LoadQueryAliasResolutionContextImpl(
|
||||||
|
sessionFactory(),
|
||||||
|
0,
|
||||||
|
Collections.singletonMap( plan.getReturns().get( 0 ), new String[] { "abc" } )
|
||||||
|
);
|
||||||
|
final EntityLoadQueryBuilderImpl queryBuilder = new EntityLoadQueryBuilderImpl(
|
||||||
|
LoadQueryInfluencers.NONE,
|
||||||
|
plan
|
||||||
|
);
|
||||||
|
final String sql = queryBuilder.generateSql( 1, sessionFactory(), aliasResolutionContext );
|
||||||
|
|
||||||
|
final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan );
|
||||||
|
final List results = new ArrayList();
|
||||||
|
|
||||||
|
final Session workSession = openSession();
|
||||||
|
workSession.beginTransaction();
|
||||||
|
workSession.doWork(
|
||||||
|
new Work() {
|
||||||
|
@Override
|
||||||
|
public void execute(Connection connection) throws SQLException {
|
||||||
|
System.out.println( "SQL : " + sql );
|
||||||
|
PreparedStatement ps = connection.prepareStatement( sql );
|
||||||
|
callback.bind( ps );
|
||||||
|
ResultSet resultSet = ps.executeQuery();
|
||||||
|
//callback.beforeExtractResults( workSession );
|
||||||
|
results.addAll(
|
||||||
|
resultSetProcessor.extractResults(
|
||||||
|
NoOpLoadPlanAdvisor.INSTANCE,
|
||||||
|
resultSet,
|
||||||
|
(SessionImplementor) workSession,
|
||||||
|
callback.getQueryParameters(),
|
||||||
|
new NamedParameterContext() {
|
||||||
|
@Override
|
||||||
|
public int[] getNamedParameterLocations(String name) {
|
||||||
|
return new int[0];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
aliasResolutionContext,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
);
|
||||||
|
resultSet.close();
|
||||||
|
ps.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
workSession.getTransaction().commit();
|
||||||
|
workSession.close();
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private interface Callback {
|
||||||
|
void bind(PreparedStatement ps) throws SQLException;
|
||||||
|
QueryParameters getQueryParameters();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue