Implement support for delayed collections with FKs based on non-primary keys
This commit is contained in:
parent
350fd81cf5
commit
a3d2f1937e
|
@ -1003,7 +1003,18 @@ public class MappingModelCreationHelper {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ModelPart fkTarget = referencedEntityDescriptor.getIdentifierMapping();
|
final ModelPart fkTarget;
|
||||||
|
if ( bootValueMapping.isReferenceToPrimaryKey() ) {
|
||||||
|
fkTarget = referencedEntityDescriptor.getIdentifierMapping();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO: need some kind of virtual model part for this
|
||||||
|
// fkTarget = attributeMapping;//bootValueMapping.;
|
||||||
|
throw new NotYetImplementedFor6Exception(
|
||||||
|
"Support for non-pk foreign-keys not yet implemented: " +
|
||||||
|
bootProperty.getPersistentClass().getEntityName() + " -> " + bootProperty.getName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if ( fkTarget instanceof BasicValuedModelPart ) {
|
if ( fkTarget instanceof BasicValuedModelPart ) {
|
||||||
final BasicValuedModelPart simpleFkTarget = (BasicValuedModelPart) fkTarget;
|
final BasicValuedModelPart simpleFkTarget = (BasicValuedModelPart) fkTarget;
|
||||||
|
@ -1049,7 +1060,7 @@ public class MappingModelCreationHelper {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new NotYetImplementedFor6Exception(
|
throw new NotYetImplementedFor6Exception(
|
||||||
"Support for" + fkTarget.getClass() + " foreign-keys not yet implemented: " +
|
"Support for " + fkTarget.getClass() + " foreign-keys not yet implemented: " +
|
||||||
bootProperty.getPersistentClass().getEntityName() + " -> " + bootProperty.getName()
|
bootProperty.getPersistentClass().getEntityName() + " -> " + bootProperty.getName()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -518,10 +518,19 @@ public class PluralAttributeMappingImpl
|
||||||
return new SelectEagerCollectionFetch( fetchablePath, this, fetchParent );
|
return new SelectEagerCollectionFetch( fetchablePath, this, fetchParent );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final FromClauseAccess fromClauseAccess = sqlAstCreationState.getFromClauseAccess();
|
||||||
|
final TableGroup fetchParentTableGroup = fromClauseAccess.getTableGroup( fetchParent.getNavigablePath() );
|
||||||
|
final DomainResult fkResult = getKeyDescriptor().createDomainResult(
|
||||||
|
fetchablePath,
|
||||||
|
fetchParentTableGroup,
|
||||||
|
false,
|
||||||
|
creationState
|
||||||
|
);
|
||||||
return new DelayedCollectionFetch(
|
return new DelayedCollectionFetch(
|
||||||
fetchablePath,
|
fetchablePath,
|
||||||
this,
|
this,
|
||||||
fetchParent
|
fetchParent,
|
||||||
|
fkResult
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1406,9 +1406,21 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
final int sqmPosition = (Integer) literal;
|
final int sqmPosition = (Integer) literal;
|
||||||
final List<SqlSelection> selections = currentSqlSelectionCollector().getSelections( sqmPosition );
|
final List<SqlSelection> selections = currentSqlSelectionCollector().getSelections( sqmPosition );
|
||||||
final List<Expression> expressions = new ArrayList<>( selections.size() );
|
final List<Expression> expressions = new ArrayList<>( selections.size() );
|
||||||
for ( SqlSelection selection : selections ) {
|
OUTER: for ( int i = 0; i < selections.size(); i++ ) {
|
||||||
|
final SqlSelection selection = selections.get( i );
|
||||||
|
// We skip duplicate selections which can occur when grouping/ordering by an entity alias.
|
||||||
|
// Duplication happens because the primary key of an entity usually acts as FK target of collections
|
||||||
|
// which is, just like the identifier itself, also registered as selection
|
||||||
|
for ( int j = 0; j < i; j++ ) {
|
||||||
|
if ( selections.get( j ) == selection ) {
|
||||||
|
continue OUTER;
|
||||||
|
}
|
||||||
|
}
|
||||||
expressions.add( new SqlSelectionExpression( selection ) );
|
expressions.add( new SqlSelectionExpression( selection ) );
|
||||||
}
|
}
|
||||||
|
if ( expressions.size() == 1 ) {
|
||||||
|
return expressions.get( 0 );
|
||||||
|
}
|
||||||
return new SqlTuple( expressions, null );
|
return new SqlTuple( expressions, null );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ import java.util.function.Consumer;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||||
|
import org.hibernate.sql.results.graph.DomainResult;
|
||||||
|
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||||
import org.hibernate.sql.results.graph.Initializer;
|
import org.hibernate.sql.results.graph.Initializer;
|
||||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||||
|
@ -23,13 +25,22 @@ public class DelayedCollectionAssembler extends AbstractCollectionAssembler {
|
||||||
NavigablePath fetchPath,
|
NavigablePath fetchPath,
|
||||||
PluralAttributeMapping fetchedMapping,
|
PluralAttributeMapping fetchedMapping,
|
||||||
FetchParentAccess parentAccess,
|
FetchParentAccess parentAccess,
|
||||||
|
DomainResult fkResult,
|
||||||
AssemblerCreationState creationState) {
|
AssemblerCreationState creationState) {
|
||||||
super(
|
super(
|
||||||
fetchedMapping,
|
fetchedMapping,
|
||||||
() -> (CollectionInitializer) creationState.resolveInitializer(
|
() -> (CollectionInitializer) creationState.resolveInitializer(
|
||||||
fetchPath,
|
fetchPath,
|
||||||
fetchedMapping,
|
fetchedMapping,
|
||||||
() -> new DelayedCollectionInitializer( fetchPath, fetchedMapping, parentAccess )
|
() -> {
|
||||||
|
final DomainResultAssembler fkAssembler = fkResult.createResultAssembler( creationState );
|
||||||
|
return new DelayedCollectionInitializer(
|
||||||
|
fetchPath,
|
||||||
|
fetchedMapping,
|
||||||
|
parentAccess,
|
||||||
|
fkAssembler
|
||||||
|
);
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,6 +12,7 @@ import org.hibernate.engine.FetchTiming;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||||
|
import org.hibernate.sql.results.graph.DomainResult;
|
||||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||||
import org.hibernate.sql.results.graph.FetchParent;
|
import org.hibernate.sql.results.graph.FetchParent;
|
||||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||||
|
@ -22,11 +23,16 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class DelayedCollectionFetch extends CollectionFetch {
|
public class DelayedCollectionFetch extends CollectionFetch {
|
||||||
|
|
||||||
|
private final DomainResult fkResult;
|
||||||
|
|
||||||
public DelayedCollectionFetch(
|
public DelayedCollectionFetch(
|
||||||
NavigablePath fetchedPath,
|
NavigablePath fetchedPath,
|
||||||
PluralAttributeMapping fetchedAttribute,
|
PluralAttributeMapping fetchedAttribute,
|
||||||
FetchParent fetchParent) {
|
FetchParent fetchParent,
|
||||||
|
DomainResult fkResult) {
|
||||||
super( fetchedPath, fetchedAttribute, fetchParent );
|
super( fetchedPath, fetchedAttribute, fetchParent );
|
||||||
|
this.fkResult = fkResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -37,6 +43,7 @@ public class DelayedCollectionFetch extends CollectionFetch {
|
||||||
getNavigablePath(),
|
getNavigablePath(),
|
||||||
getFetchedMapping(),
|
getFetchedMapping(),
|
||||||
parentAccess,
|
parentAccess,
|
||||||
|
fkResult,
|
||||||
creationState
|
creationState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.sql.results.graph.collection.internal;
|
||||||
|
|
||||||
import org.hibernate.collection.spi.CollectionSemantics;
|
import org.hibernate.collection.spi.CollectionSemantics;
|
||||||
import org.hibernate.collection.spi.PersistentCollection;
|
import org.hibernate.collection.spi.PersistentCollection;
|
||||||
|
import org.hibernate.engine.spi.CollectionKey;
|
||||||
import org.hibernate.engine.spi.PersistenceContext;
|
import org.hibernate.engine.spi.PersistenceContext;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.internal.log.LoggingHelper;
|
import org.hibernate.internal.log.LoggingHelper;
|
||||||
|
@ -15,8 +16,10 @@ import org.hibernate.metamodel.CollectionClassification;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
|
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||||
import org.hibernate.sql.results.graph.collection.LoadingCollectionEntry;
|
import org.hibernate.sql.results.graph.collection.LoadingCollectionEntry;
|
||||||
|
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,11 +27,67 @@ import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||||
*/
|
*/
|
||||||
public class DelayedCollectionInitializer extends AbstractCollectionInitializer {
|
public class DelayedCollectionInitializer extends AbstractCollectionInitializer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* refers to the collection's container value - which collection-key?
|
||||||
|
*/
|
||||||
|
private final DomainResultAssembler keyContainerAssembler;
|
||||||
|
|
||||||
public DelayedCollectionInitializer(
|
public DelayedCollectionInitializer(
|
||||||
NavigablePath fetchedPath,
|
NavigablePath fetchedPath,
|
||||||
PluralAttributeMapping fetchedMapping,
|
PluralAttributeMapping fetchedMapping,
|
||||||
FetchParentAccess parentAccess) {
|
FetchParentAccess parentAccess,
|
||||||
|
DomainResultAssembler keyContainerAssembler) {
|
||||||
super( fetchedPath, fetchedMapping, parentAccess );
|
super( fetchedPath, fetchedMapping, parentAccess );
|
||||||
|
this.keyContainerAssembler = keyContainerAssembler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resolveKey(RowProcessingState rowProcessingState) {
|
||||||
|
if ( collectionKey != null ) {
|
||||||
|
// already resolved
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final CollectionKey loadingKey = rowProcessingState.getCollectionKey();
|
||||||
|
if ( loadingKey != null ) {
|
||||||
|
collectionKey = loadingKey;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final JdbcValuesSourceProcessingOptions processingOptions = rowProcessingState.getJdbcValuesSourceProcessingState()
|
||||||
|
.getProcessingOptions();
|
||||||
|
|
||||||
|
final Object keyContainerValue = keyContainerAssembler.assemble(
|
||||||
|
rowProcessingState,
|
||||||
|
processingOptions
|
||||||
|
);
|
||||||
|
if ( keyContainerValue != null ) {
|
||||||
|
this.collectionKey = new CollectionKey(
|
||||||
|
collectionAttributeMapping.getCollectionDescriptor(),
|
||||||
|
keyContainerValue
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: This fails e.g. EagerCollectionLazyKeyManyToOneTest because Order$Id#customer is null
|
||||||
|
// which is required for the hash code. Is this being null at this point a bug?
|
||||||
|
// if ( CollectionLoadingLogger.DEBUG_ENABLED ) {
|
||||||
|
// CollectionLoadingLogger.INSTANCE.debugf(
|
||||||
|
// "(%s) Current row collection key : %s",
|
||||||
|
// DelayedCollectionInitializer.class.getSimpleName(),
|
||||||
|
// LoggingHelper.toLoggableString( getNavigablePath(), this.collectionKey.getKey() )
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
parentAccess.registerResolutionListener( owner -> collectionInstance.setOwner( owner ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final Object parentKey = parentAccess.getParentKey();
|
||||||
|
if ( parentKey != null ) {
|
||||||
|
this.collectionKey = new CollectionKey(
|
||||||
|
collectionAttributeMapping.getCollectionDescriptor(),
|
||||||
|
parentKey
|
||||||
|
);
|
||||||
|
parentAccess.registerResolutionListener( owner -> collectionInstance.setOwner( owner ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue