Add AssociationKey for bidirectionality detection
This commit is contained in:
parent
511d4d55cd
commit
ead64b3ec9
|
@ -8,6 +8,7 @@ package org.hibernate.loader.ast.internal;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -33,6 +34,7 @@ import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor;
|
import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor;
|
||||||
import org.hibernate.metamodel.mapping.ordering.OrderByFragment;
|
import org.hibernate.metamodel.mapping.ordering.OrderByFragment;
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
|
@ -80,6 +82,8 @@ import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnRefere
|
||||||
public class LoaderSelectBuilder {
|
public class LoaderSelectBuilder {
|
||||||
private static final Logger log = Logger.getLogger( LoaderSelectBuilder.class );
|
private static final Logger log = Logger.getLogger( LoaderSelectBuilder.class );
|
||||||
|
|
||||||
|
private HashMap<String, List<Fetch>> visitedNavigablePath = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an SQL AST select-statement based on matching one-or-more keys
|
* Create an SQL AST select-statement based on matching one-or-more keys
|
||||||
*
|
*
|
||||||
|
@ -209,7 +213,7 @@ public class LoaderSelectBuilder {
|
||||||
new SimpleFromClauseAccessImpl(),
|
new SimpleFromClauseAccessImpl(),
|
||||||
lockOptions,
|
lockOptions,
|
||||||
this::visitFetches,
|
this::visitFetches,
|
||||||
numberOfKeysToLoad > 1,
|
numberOfKeysToLoad > 1 || restrictedPart instanceof ForeignKeyDescriptor,
|
||||||
creationContext
|
creationContext
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -430,15 +434,22 @@ public class LoaderSelectBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Fetch> fetches = new ArrayList<>();
|
final List<Fetch> fetches = new ArrayList<>();
|
||||||
|
String fullPath = fetchParent.getNavigablePath().getFullPath();
|
||||||
|
final List<Fetch> fullPathFetches = visitedNavigablePath.get( fullPath );
|
||||||
|
if ( fullPathFetches != null ) {
|
||||||
|
return fullPathFetches;
|
||||||
|
}
|
||||||
|
|
||||||
final BiConsumer<Fetchable, Boolean> processor = createFetchableBiConsumer( fetchParent, querySpec, creationState, fetches );
|
final BiConsumer<Fetchable, Boolean> processor = createFetchableBiConsumer( fetchParent, querySpec, creationState, fetches );
|
||||||
|
|
||||||
final FetchableContainer referencedMappingContainer = fetchParent.getReferencedMappingContainer();
|
final FetchableContainer referencedMappingContainer = fetchParent.getReferencedMappingContainer();
|
||||||
if ( fetchParent.getNavigablePath().getParent() != null ) {
|
if ( fetchParent.getNavigablePath().getParent() != null ) {
|
||||||
referencedMappingContainer.visitKeyFetchables( fetchable -> processor.accept( fetchable, true ), null );
|
referencedMappingContainer.visitKeyFetchables(
|
||||||
|
fetchable -> processor.accept( fetchable, true ), null );
|
||||||
}
|
}
|
||||||
referencedMappingContainer.visitFetchables( fetchable -> processor.accept( fetchable, false ), null );
|
referencedMappingContainer.visitFetchables(
|
||||||
|
fetchable -> processor.accept( fetchable, false ), null );
|
||||||
|
visitedNavigablePath.put( fullPath, fetches );
|
||||||
return fetches;
|
return fetches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,12 +512,14 @@ public class LoaderSelectBuilder {
|
||||||
joined = false;
|
joined = false;
|
||||||
}
|
}
|
||||||
else if ( fetchDepth > maximumFetchDepth ) {
|
else if ( fetchDepth > maximumFetchDepth ) {
|
||||||
return;
|
if ( !( fetchable instanceof BasicValuedModelPart ) && !( fetchable instanceof EmbeddedAttributeMapping ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ( !( fetchable instanceof BasicValuedModelPart ) ) {
|
if ( !( fetchable instanceof BasicValuedModelPart ) && !( fetchable instanceof EmbeddedAttributeMapping ) ) {
|
||||||
fetchDepth++;
|
fetchDepth++;
|
||||||
}
|
}
|
||||||
final Fetch fetch = fetchable.generateFetch(
|
final Fetch fetch = fetchable.generateFetch(
|
||||||
|
@ -537,7 +550,7 @@ public class LoaderSelectBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if ( !( fetchable instanceof BasicValuedModelPart ) ) {
|
if ( !( fetchable instanceof BasicValuedModelPart ) && !( fetchable instanceof EmbeddedAttributeMapping ) ) {
|
||||||
fetchDepth--;
|
fetchDepth--;
|
||||||
}
|
}
|
||||||
if ( entityGraphTraversalState != null ) {
|
if ( entityGraphTraversalState != null ) {
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
package org.hibernate.loader.ast.internal;
|
package org.hibernate.loader.ast.internal;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import javax.persistence.CacheRetrieveMode;
|
import javax.persistence.CacheRetrieveMode;
|
||||||
import javax.persistence.CacheStoreMode;
|
import javax.persistence.CacheStoreMode;
|
||||||
|
|
||||||
|
@ -16,6 +18,7 @@ import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.graph.spi.AppliedGraph;
|
import org.hibernate.graph.spi.AppliedGraph;
|
||||||
|
import org.hibernate.metamodel.mapping.AssociationKey;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.query.Limit;
|
import org.hibernate.query.Limit;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
|
@ -48,13 +51,15 @@ public class LoaderSqlAstCreationState
|
||||||
|
|
||||||
private final QuerySpec querySpec;
|
private final QuerySpec querySpec;
|
||||||
private final SqlAliasBaseManager sqlAliasBaseManager;
|
private final SqlAliasBaseManager sqlAliasBaseManager;
|
||||||
private boolean forceIdentifierSelection;
|
private final boolean forceIdentifierSelection;
|
||||||
private final SqlAstCreationContext sf;
|
private final SqlAstCreationContext sf;
|
||||||
private final SqlAstQuerySpecProcessingStateImpl processingState;
|
private final SqlAstQuerySpecProcessingStateImpl processingState;
|
||||||
private final FromClauseAccess fromClauseAccess;
|
private final FromClauseAccess fromClauseAccess;
|
||||||
private final LockOptions lockOptions;
|
private final LockOptions lockOptions;
|
||||||
private final FetchProcessor fetchProcessor;
|
private final FetchProcessor fetchProcessor;
|
||||||
|
|
||||||
|
private Set<AssociationKey> visitedAssociationKeys = new HashSet<>();
|
||||||
|
|
||||||
public LoaderSqlAstCreationState(
|
public LoaderSqlAstCreationState(
|
||||||
QuerySpec querySpec,
|
QuerySpec querySpec,
|
||||||
SqlAliasBaseManager sqlAliasBaseManager,
|
SqlAliasBaseManager sqlAliasBaseManager,
|
||||||
|
@ -143,6 +148,16 @@ public class LoaderSqlAstCreationState
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerVisitedAssociationKey(AssociationKey associationKey) {
|
||||||
|
visitedAssociationKeys.add( associationKey );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAssociationKeyVisited(AssociationKey associationKey) {
|
||||||
|
return visitedAssociationKeys.contains( associationKey );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ModelPart resolveModelPart(NavigablePath navigablePath) {
|
public ModelPart resolveModelPart(NavigablePath navigablePath) {
|
||||||
// for now, let's assume that the navigable-path refers to TableGroup
|
// for now, let's assume that the navigable-path refers to TableGroup
|
||||||
|
|
|
@ -20,7 +20,9 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.loader.ast.spi.SingleUniqueKeyEntityLoader;
|
import org.hibernate.loader.ast.spi.SingleUniqueKeyEntityLoader;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||||
import org.hibernate.query.spi.QueryOptions;
|
import org.hibernate.query.spi.QueryOptions;
|
||||||
import org.hibernate.query.spi.QueryOptionsAdapter;
|
import org.hibernate.query.spi.QueryOptionsAdapter;
|
||||||
import org.hibernate.query.spi.QueryParameterBindings;
|
import org.hibernate.query.spi.QueryParameterBindings;
|
||||||
|
@ -40,13 +42,18 @@ import org.hibernate.sql.exec.spi.JdbcSelect;
|
||||||
*/
|
*/
|
||||||
public class SingleUniqueKeyEntityLoaderStandard<T> implements SingleUniqueKeyEntityLoader<T> {
|
public class SingleUniqueKeyEntityLoaderStandard<T> implements SingleUniqueKeyEntityLoader<T> {
|
||||||
private final EntityMappingType entityDescriptor;
|
private final EntityMappingType entityDescriptor;
|
||||||
private final SingularAttributeMapping uniqueKeyAttribute;
|
private final ModelPart uniqueKeyAttribute;
|
||||||
|
|
||||||
public SingleUniqueKeyEntityLoaderStandard(
|
public SingleUniqueKeyEntityLoaderStandard(
|
||||||
EntityMappingType entityDescriptor,
|
EntityMappingType entityDescriptor,
|
||||||
SingularAttributeMapping uniqueKeyAttribute) {
|
SingularAttributeMapping uniqueKeyAttribute) {
|
||||||
this.entityDescriptor = entityDescriptor;
|
this.entityDescriptor = entityDescriptor;
|
||||||
this.uniqueKeyAttribute = uniqueKeyAttribute;
|
if ( uniqueKeyAttribute instanceof ToOneAttributeMapping ) {
|
||||||
|
this.uniqueKeyAttribute = ( (ToOneAttributeMapping) uniqueKeyAttribute ).getForeignKeyDescriptor();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.uniqueKeyAttribute = uniqueKeyAttribute;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -142,7 +149,11 @@ public class SingleUniqueKeyEntityLoaderStandard<T> implements SingleUniqueKeyEn
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
assert list.size() == 1;
|
int size = list.size();
|
||||||
|
assert size <= 1;
|
||||||
|
if ( size == 0 ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
return (T) list.get( 0 );
|
return (T) list.get( 0 );
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.metamodel.mapping;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Andrea Boriero
|
||||||
|
*/
|
||||||
|
public class AssociationKey {
|
||||||
|
private final String table;
|
||||||
|
private final List<String> columns;
|
||||||
|
|
||||||
|
public AssociationKey(String table, List<String> columns) {
|
||||||
|
this.table = table;
|
||||||
|
this.columns = columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if ( this == o ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ( o == null || getClass() != o.getClass() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final AssociationKey that = (AssociationKey) o;
|
||||||
|
return table.equals( that.table ) && columns.equals( that.columns );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return table.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String str;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if ( str == null ) {
|
||||||
|
str = "AssociationKey(table=" + table + ", columns={" + String.join( ",", columns ) + "})";
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -345,7 +345,18 @@ public class EmbeddableMappingType implements ManagedMappingType {
|
||||||
Clause clause,
|
Clause clause,
|
||||||
TypeConfiguration typeConfiguration) {
|
TypeConfiguration typeConfiguration) {
|
||||||
attributeMappings.forEach(
|
attributeMappings.forEach(
|
||||||
(s, attributeMapping) -> attributeMapping.visitJdbcTypes( action, clause, typeConfiguration )
|
(s, attributeMapping) -> {
|
||||||
|
if ( attributeMapping instanceof ToOneAttributeMapping ) {
|
||||||
|
( (ToOneAttributeMapping) attributeMapping ).getKeyTargetMatchPart().visitJdbcTypes(
|
||||||
|
action,
|
||||||
|
clause,
|
||||||
|
typeConfiguration
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
attributeMapping.visitJdbcTypes( action, clause, typeConfiguration );
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,12 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.metamodel.mapping;
|
package org.hibernate.metamodel.mapping;
|
||||||
|
|
||||||
import org.hibernate.sql.results.graph.Fetchable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commonality between `many-to-one`, `one-to-one` and `any`, as well as entity-valued collection elements and map-keys
|
* Commonality between `many-to-one`, `one-to-one` and `any`, as well as entity-valued collection elements and map-keys
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface EntityAssociationMapping extends ModelPart, Fetchable {
|
public interface EntityAssociationMapping extends ModelPart, Association {
|
||||||
@Override
|
@Override
|
||||||
default String getFetchableName() {
|
default String getFetchableName() {
|
||||||
return getPartName();
|
return getPartName();
|
||||||
|
|
|
@ -202,7 +202,6 @@ public interface EntityMappingType extends ManagedMappingType, Loadable {
|
||||||
final Object value = assembler == null ? UNFETCHED_PROPERTY : assembler.assemble( rowProcessingState );
|
final Object value = assembler == null ? UNFETCHED_PROPERTY : assembler.assemble( rowProcessingState );
|
||||||
|
|
||||||
values[index++] = value;
|
values[index++] = value;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -51,10 +51,6 @@ public interface ForeignKeyDescriptor extends VirtualModelPart {
|
||||||
return PART_NAME;
|
return PART_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getReferringTableExpression();
|
|
||||||
|
|
||||||
String getTargetTableExpression();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits the FK "referring" columns
|
* Visits the FK "referring" columns
|
||||||
*/
|
*/
|
||||||
|
@ -67,6 +63,5 @@ public interface ForeignKeyDescriptor extends VirtualModelPart {
|
||||||
|
|
||||||
void visitTargetColumns(ColumnConsumer consumer);
|
void visitTargetColumns(ColumnConsumer consumer);
|
||||||
|
|
||||||
|
AssociationKey getAssociationKey();
|
||||||
boolean areTargetColumnNamesEqualsTo(String[] columnNames);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,4 +55,6 @@ public interface PluralAttributeMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
String getSeparateCollectionTable();
|
String getSeparateCollectionTable();
|
||||||
|
|
||||||
|
String getMappedBy();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
package org.hibernate.metamodel.mapping.internal;
|
package org.hibernate.metamodel.mapping.internal;
|
||||||
|
|
||||||
import org.hibernate.engine.FetchStrategy;
|
import org.hibernate.engine.FetchStrategy;
|
||||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
|
||||||
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
||||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||||
|
|
|
@ -98,6 +98,7 @@ public class EmbeddedAttributeMapping
|
||||||
public EmbeddableMappingType getEmbeddableTypeDescriptor() {
|
public EmbeddableMappingType getEmbeddableTypeDescriptor() {
|
||||||
return embeddableMappingType;
|
return embeddableMappingType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SingularAttributeMapping getParentInjectionAttributeMapping() {
|
public SingularAttributeMapping getParentInjectionAttributeMapping() {
|
||||||
// todo (6.0) : implement
|
// todo (6.0) : implement
|
||||||
|
|
|
@ -8,8 +8,10 @@ package org.hibernate.metamodel.mapping.internal;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.metamodel.mapping.AssociationKey;
|
||||||
import org.hibernate.metamodel.mapping.ColumnConsumer;
|
import org.hibernate.metamodel.mapping.ColumnConsumer;
|
||||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
@ -20,6 +22,7 @@ import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||||
import org.hibernate.query.ComparisonOperator;
|
import org.hibernate.query.ComparisonOperator;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||||
|
@ -49,6 +52,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor, Model
|
||||||
private final List<String> targetColumnExpressions;
|
private final List<String> targetColumnExpressions;
|
||||||
private final EmbeddableValuedModelPart mappingType;
|
private final EmbeddableValuedModelPart mappingType;
|
||||||
private final List<JdbcMapping> jdbcMappings;
|
private final List<JdbcMapping> jdbcMappings;
|
||||||
|
private AssociationKey associationKey;
|
||||||
|
|
||||||
public EmbeddedForeignKeyDescriptor(
|
public EmbeddedForeignKeyDescriptor(
|
||||||
EmbeddedIdentifierMappingImpl mappingType,
|
EmbeddedIdentifierMappingImpl mappingType,
|
||||||
|
@ -305,16 +309,6 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor, Model
|
||||||
throw new IllegalStateException( "Could not resolve binding for table `" + table + "`" );
|
throw new IllegalStateException( "Could not resolve binding for table `" + table + "`" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getReferringTableExpression() {
|
|
||||||
return keyColumnContainingTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTargetTableExpression() {
|
|
||||||
return targetColumnContainingTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitReferringColumns(ColumnConsumer consumer) {
|
public void visitReferringColumns(ColumnConsumer consumer) {
|
||||||
for ( int i = 0; i < keyColumnExpressions.size(); i++ ) {
|
for ( int i = 0; i < keyColumnExpressions.size(); i++ ) {
|
||||||
|
@ -330,17 +324,11 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor, Model
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean areTargetColumnNamesEqualsTo(String[] columnNames) {
|
public AssociationKey getAssociationKey() {
|
||||||
int length = columnNames.length;
|
if ( associationKey == null ) {
|
||||||
if ( length != targetColumnExpressions.size() ) {
|
associationKey = new AssociationKey( keyColumnContainingTable, keyColumnExpressions );
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
for ( int i = 0; i < length; i++ ) {
|
return associationKey;
|
||||||
if ( !targetColumnExpressions.contains( columnNames[i] ) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -363,4 +351,9 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor, Model
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitJdbcTypes(
|
||||||
|
Consumer<JdbcMapping> action, Clause clause, TypeConfiguration typeConfiguration) {
|
||||||
|
mappingType.visitJdbcTypes( action, clause, typeConfiguration );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||||
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.ast.spi.FromClauseAccess;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
import org.hibernate.sql.results.graph.DomainResult;
|
import org.hibernate.sql.results.graph.DomainResult;
|
||||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||||
|
@ -38,7 +39,7 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class EntityCollectionPart
|
public class EntityCollectionPart
|
||||||
implements CollectionPart, EntityAssociationMapping, EntityValuedFetchable, Association, FetchOptions {
|
implements CollectionPart, EntityAssociationMapping, EntityValuedFetchable, FetchOptions {
|
||||||
private final NavigableRole navigableRole;
|
private final NavigableRole navigableRole;
|
||||||
private final CollectionPersister collectionDescriptor;
|
private final CollectionPersister collectionDescriptor;
|
||||||
private final Nature nature;
|
private final Nature nature;
|
||||||
|
@ -137,18 +138,17 @@ public class EntityCollectionPart
|
||||||
LockMode lockMode,
|
LockMode lockMode,
|
||||||
String resultVariable,
|
String resultVariable,
|
||||||
DomainResultCreationState creationState) {
|
DomainResultCreationState creationState) {
|
||||||
// assert fetchParent.getReferencedMappingContainer() instanceof PluralAttributeMapping;
|
|
||||||
|
|
||||||
// find or create the TableGroup associated with this `fetchablePath`
|
// find or create the TableGroup associated with this `fetchablePath`
|
||||||
creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup(
|
final FromClauseAccess fromClauseAccess = creationState.getSqlAstCreationState().getFromClauseAccess();
|
||||||
|
creationState.registerVisitedAssociationKey( getForeignKeyDescriptor().getAssociationKey() );
|
||||||
|
|
||||||
|
fromClauseAccess.resolveTableGroup(
|
||||||
fetchablePath,
|
fetchablePath,
|
||||||
np -> {
|
np -> {
|
||||||
// We need to create one. The Result will be able to find it later by path
|
// We need to create one. The Result will be able to find it later by path
|
||||||
|
|
||||||
// first, find the collection's TableGroup
|
// first, find the collection's TableGroup
|
||||||
final TableGroup collectionTableGroup = creationState.getSqlAstCreationState()
|
final TableGroup collectionTableGroup = fromClauseAccess.getTableGroup( fetchParent.getNavigablePath() );
|
||||||
.getFromClauseAccess()
|
|
||||||
.getTableGroup( fetchParent.getNavigablePath() );
|
|
||||||
|
|
||||||
assert collectionTableGroup != null;
|
assert collectionTableGroup != null;
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.hibernate.jpa.spi.JpaCompliance;
|
||||||
import org.hibernate.mapping.Collection;
|
import org.hibernate.mapping.Collection;
|
||||||
import org.hibernate.mapping.IndexedCollection;
|
import org.hibernate.mapping.IndexedCollection;
|
||||||
import org.hibernate.mapping.List;
|
import org.hibernate.mapping.List;
|
||||||
|
import org.hibernate.mapping.OneToOne;
|
||||||
import org.hibernate.mapping.Property;
|
import org.hibernate.mapping.Property;
|
||||||
import org.hibernate.mapping.Value;
|
import org.hibernate.mapping.Value;
|
||||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||||
|
@ -45,6 +46,7 @@ import org.hibernate.persister.entity.Joinable;
|
||||||
import org.hibernate.property.access.spi.PropertyAccess;
|
import org.hibernate.property.access.spi.PropertyAccess;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||||
|
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasStemHelper;
|
import org.hibernate.sql.ast.spi.SqlAliasStemHelper;
|
||||||
|
@ -94,6 +96,7 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
|
||||||
private final FetchStyle fetchStyle;
|
private final FetchStyle fetchStyle;
|
||||||
|
|
||||||
private final CascadeStyle cascadeStyle;
|
private final CascadeStyle cascadeStyle;
|
||||||
|
private final String mappedBy;
|
||||||
|
|
||||||
private final CollectionPersister collectionDescriptor;
|
private final CollectionPersister collectionDescriptor;
|
||||||
private final String separateCollectionTable;
|
private final String separateCollectionTable;
|
||||||
|
@ -169,6 +172,7 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
|
||||||
this.fetchStyle = fetchStyle;
|
this.fetchStyle = fetchStyle;
|
||||||
this.cascadeStyle = cascadeStyle;
|
this.cascadeStyle = cascadeStyle;
|
||||||
this.collectionDescriptor = collectionDescriptor;
|
this.collectionDescriptor = collectionDescriptor;
|
||||||
|
this.mappedBy = bootDescriptor.getMappedByProperty();
|
||||||
|
|
||||||
this.sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( attributeName );
|
this.sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( attributeName );
|
||||||
|
|
||||||
|
@ -380,6 +384,11 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
|
||||||
return separateCollectionTable;
|
return separateCollectionTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMappedBy() {
|
||||||
|
return mappedBy;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getStateArrayPosition() {
|
public int getStateArrayPosition() {
|
||||||
return stateArrayPosition;
|
return stateArrayPosition;
|
||||||
|
@ -442,12 +451,15 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
|
||||||
DomainResultCreationState creationState) {
|
DomainResultCreationState creationState) {
|
||||||
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
|
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
|
||||||
|
|
||||||
|
creationState.registerVisitedAssociationKey( fkDescriptor.getAssociationKey() );
|
||||||
|
|
||||||
if ( fetchTiming == FetchTiming.IMMEDIATE) {
|
if ( fetchTiming == FetchTiming.IMMEDIATE) {
|
||||||
if ( selected ) {
|
if ( selected ) {
|
||||||
final TableGroup collectionTableGroup = sqlAstCreationState.getFromClauseAccess().resolveTableGroup(
|
final FromClauseAccess fromClauseAccess = sqlAstCreationState.getFromClauseAccess();
|
||||||
|
final TableGroup collectionTableGroup = fromClauseAccess.resolveTableGroup(
|
||||||
fetchablePath,
|
fetchablePath,
|
||||||
p -> {
|
p -> {
|
||||||
final TableGroup lhsTableGroup = sqlAstCreationState.getFromClauseAccess().getTableGroup(
|
final TableGroup lhsTableGroup = fromClauseAccess.getTableGroup(
|
||||||
fetchParent.getNavigablePath() );
|
fetchParent.getNavigablePath() );
|
||||||
final TableGroupJoin tableGroupJoin = createTableGroupJoin(
|
final TableGroupJoin tableGroupJoin = createTableGroupJoin(
|
||||||
fetchablePath,
|
fetchablePath,
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.metamodel.mapping.internal;
|
package org.hibernate.metamodel.mapping.internal;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
@ -14,6 +15,7 @@ import org.hibernate.LockMode;
|
||||||
import org.hibernate.engine.FetchStyle;
|
import org.hibernate.engine.FetchStyle;
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchTiming;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
import org.hibernate.metamodel.mapping.AssociationKey;
|
||||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||||
import org.hibernate.metamodel.mapping.ColumnConsumer;
|
import org.hibernate.metamodel.mapping.ColumnConsumer;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
@ -53,6 +55,7 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
|
||||||
private final String targetColumnContainingTable;
|
private final String targetColumnContainingTable;
|
||||||
private final String targetColumnExpression;
|
private final String targetColumnExpression;
|
||||||
private final JdbcMapping jdbcMapping;
|
private final JdbcMapping jdbcMapping;
|
||||||
|
private AssociationKey associationKey;
|
||||||
|
|
||||||
public SimpleForeignKeyDescriptor(
|
public SimpleForeignKeyDescriptor(
|
||||||
String keyColumnContainingTable,
|
String keyColumnContainingTable,
|
||||||
|
@ -285,32 +288,23 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getReferringTableExpression() {
|
|
||||||
return keyColumnContainingTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitReferringColumns(ColumnConsumer consumer) {
|
public void visitReferringColumns(ColumnConsumer consumer) {
|
||||||
consumer.accept( keyColumnContainingTable, keyColumnExpression, jdbcMapping );
|
consumer.accept( keyColumnContainingTable, keyColumnExpression, jdbcMapping );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTargetTableExpression() {
|
|
||||||
return targetColumnContainingTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitTargetColumns(ColumnConsumer consumer) {
|
public void visitTargetColumns(ColumnConsumer consumer) {
|
||||||
consumer.accept( targetColumnContainingTable, targetColumnExpression, jdbcMapping );
|
consumer.accept( targetColumnContainingTable, targetColumnExpression, jdbcMapping );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean areTargetColumnNamesEqualsTo(String[] columnNames) {
|
public AssociationKey getAssociationKey() {
|
||||||
if ( columnNames.length != 1 ) {
|
if ( associationKey == null ) {
|
||||||
return false;
|
final List<String> associationKeyColumns = Collections.singletonList( keyColumnExpression );
|
||||||
|
associationKey = new AssociationKey( keyColumnContainingTable, associationKeyColumns );
|
||||||
}
|
}
|
||||||
return targetColumnExpression.equals( columnNames[0] );
|
return associationKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -393,11 +387,9 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
|
||||||
return jdbcMapping;
|
return jdbcMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTargetColumnContainingTable() {
|
@Override
|
||||||
return targetColumnContainingTable;
|
public String toString() {
|
||||||
}
|
return "SimpleForeignKeyDescriptor : " + keyColumnContainingTable + "." + keyColumnExpression
|
||||||
|
+ " --> " + targetColumnContainingTable + "." + targetColumnExpression;
|
||||||
public String getTargetColumnExpression() {
|
|
||||||
return targetColumnExpression;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,23 +6,24 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.metamodel.mapping.internal;
|
package org.hibernate.metamodel.mapping.internal;
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.engine.FetchStrategy;
|
import org.hibernate.engine.FetchStrategy;
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchTiming;
|
||||||
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.mapping.ManyToOne;
|
import org.hibernate.mapping.ManyToOne;
|
||||||
import org.hibernate.mapping.OneToOne;
|
import org.hibernate.mapping.OneToOne;
|
||||||
import org.hibernate.mapping.ToOne;
|
import org.hibernate.mapping.ToOne;
|
||||||
import org.hibernate.metamodel.mapping.Association;
|
import org.hibernate.metamodel.mapping.AssociationKey;
|
||||||
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||||
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.persister.entity.SingleTableEntityPersister;
|
|
||||||
import org.hibernate.property.access.spi.PropertyAccess;
|
import org.hibernate.property.access.spi.PropertyAccess;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||||
|
@ -42,21 +43,21 @@ import org.hibernate.sql.results.graph.DomainResult;
|
||||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||||
import org.hibernate.sql.results.graph.Fetch;
|
import org.hibernate.sql.results.graph.Fetch;
|
||||||
import org.hibernate.sql.results.graph.FetchParent;
|
import org.hibernate.sql.results.graph.FetchParent;
|
||||||
import org.hibernate.sql.results.graph.Fetchable;
|
import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable;
|
||||||
import org.hibernate.sql.results.graph.entity.EntityFetch;
|
import org.hibernate.sql.results.graph.entity.EntityFetch;
|
||||||
import org.hibernate.sql.results.graph.entity.EntityResultGraphNode;
|
|
||||||
import org.hibernate.sql.results.graph.entity.EntityValuedFetchable;
|
import org.hibernate.sql.results.graph.entity.EntityValuedFetchable;
|
||||||
import org.hibernate.sql.results.graph.entity.internal.EntityFetchDelayedImpl;
|
import org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchImpl;
|
||||||
import org.hibernate.sql.results.graph.entity.internal.EntityFetchJoinedImpl;
|
import org.hibernate.sql.results.graph.entity.internal.EntityFetchJoinedImpl;
|
||||||
import org.hibernate.sql.results.graph.entity.internal.EntityFetchSelectImpl;
|
import org.hibernate.sql.results.graph.entity.internal.EntityFetchSelectImpl;
|
||||||
import org.hibernate.sql.results.internal.domain.BiDirectionalFetchImpl;
|
import org.hibernate.sql.results.internal.domain.CircularFetchImpl;
|
||||||
|
import org.hibernate.sql.results.internal.domain.CircularBiDirectionalFetchImpl;
|
||||||
import org.hibernate.type.ForeignKeyDirection;
|
import org.hibernate.type.ForeignKeyDirection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
||||||
implements EntityValuedFetchable, EntityAssociationMapping, Association, TableGroupJoinProducer {
|
implements EntityValuedFetchable, EntityAssociationMapping, TableGroupJoinProducer {
|
||||||
|
|
||||||
public enum Cardinality {
|
public enum Cardinality {
|
||||||
ONE_TO_ONE,
|
ONE_TO_ONE,
|
||||||
|
@ -75,6 +76,7 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
||||||
private final boolean referringPrimaryKey;
|
private final boolean referringPrimaryKey;
|
||||||
|
|
||||||
private final Cardinality cardinality;
|
private final Cardinality cardinality;
|
||||||
|
private String mappedBy;
|
||||||
|
|
||||||
private ForeignKeyDescriptor foreignKeyDescriptor;
|
private ForeignKeyDescriptor foreignKeyDescriptor;
|
||||||
private ForeignKeyDirection foreignKeyDirection;
|
private ForeignKeyDirection foreignKeyDirection;
|
||||||
|
@ -124,6 +126,13 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
||||||
else {
|
else {
|
||||||
assert bootValue instanceof OneToOne;
|
assert bootValue instanceof OneToOne;
|
||||||
cardinality = Cardinality.ONE_TO_ONE;
|
cardinality = Cardinality.ONE_TO_ONE;
|
||||||
|
String mappedByProperty = ( (OneToOne) bootValue ).getMappedByProperty();
|
||||||
|
if ( mappedByProperty == null ) {
|
||||||
|
mappedBy = StringHelper.nullIfEmpty( referencedPropertyName );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mappedBy = mappedByProperty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.navigableRole = navigableRole;
|
this.navigableRole = navigableRole;
|
||||||
|
@ -169,101 +178,142 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
||||||
NavigablePath fetchablePath,
|
NavigablePath fetchablePath,
|
||||||
FetchParent fetchParent,
|
FetchParent fetchParent,
|
||||||
DomainResultCreationState creationState) {
|
DomainResultCreationState creationState) {
|
||||||
// NOTE - a circular fetch reference ultimately needs 2 pieces of information:
|
final AssociationKey associationKey = foreignKeyDescriptor.getAssociationKey();
|
||||||
// 1) The NavigablePath that is circular (`fetchablePath`)
|
|
||||||
// 2) The NavigablePath to the entity-valued-reference that is the "other side" of the circularity
|
|
||||||
|
|
||||||
final ModelPart parentModelPart = fetchParent.getReferencedModePart();
|
if ( creationState.isAssociationKeyVisited( associationKey ) ) {
|
||||||
|
NavigablePath parent = fetchablePath.getParent();
|
||||||
if ( ! Fetchable.class.isInstance( parentModelPart ) ) {
|
ModelPart modelPart = creationState.resolveModelPart( parent );
|
||||||
// the `fetchParent` would have to be a Fetch as well for this to be circular...
|
if ( modelPart instanceof EmbeddedIdentifierMappingImpl ) {
|
||||||
return null;
|
while ( parent.getFullPath().endsWith( EntityIdentifierMapping.ROLE_LOCAL_NAME ) ) {
|
||||||
}
|
parent = parent.getParent();
|
||||||
|
|
||||||
final FetchParent associationFetchParent = fetchParent.resolveContainingAssociationParent();
|
|
||||||
if ( associationFetchParent == null ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final ModelPart referencedModePart = associationFetchParent.getReferencedModePart();
|
|
||||||
assert referencedModePart instanceof Association;
|
|
||||||
|
|
||||||
final Association associationParent = (Association) referencedModePart;
|
|
||||||
|
|
||||||
if ( foreignKeyDescriptor.equals( associationParent.getForeignKeyDescriptor() ) ) {
|
|
||||||
// we need to determine the NavigablePath referring to the entity that the bi-dir
|
|
||||||
// fetch will "return" for its Assembler. so we walk "up" the FetchParent graph
|
|
||||||
// to find the "referenced entity" reference
|
|
||||||
|
|
||||||
return createBiDirectionalFetch( fetchablePath, fetchParent );
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is the case of a JoinTable
|
|
||||||
// PARENT(id)
|
|
||||||
// PARENT_CHILD(parent_id, child_id)
|
|
||||||
// CHILD(id)
|
|
||||||
// the FKDescriptor for the association `Parent.child` will be
|
|
||||||
// PARENT_CHILD.child.id -> CHILD.id
|
|
||||||
// and the FKDescriptor for the association `Child.parent` will be
|
|
||||||
// PARENT_CHILD.parent.id -> PARENT.id
|
|
||||||
// in such a case the associationParent.getIdentifyingColumnExpressions() is PARENT_CHILD.parent_id
|
|
||||||
// while the getIdentifyingColumnExpressions for this association is PARENT_CHILD.child_id
|
|
||||||
// so we will check if the parentAssociation ForeignKey Target match with the association entity identifier table and columns
|
|
||||||
final ForeignKeyDescriptor associationParentForeignKeyDescriptor = associationParent.getForeignKeyDescriptor();
|
|
||||||
if ( referencedModePart instanceof ToOneAttributeMapping
|
|
||||||
&& ( (ToOneAttributeMapping) referencedModePart ).getDeclaringType() == getPartMappingType() ) {
|
|
||||||
if ( this.foreignKeyDescriptor.getReferringTableExpression()
|
|
||||||
.equals( associationParentForeignKeyDescriptor.getReferringTableExpression() ) ) {
|
|
||||||
final SingleTableEntityPersister entityPersister = (SingleTableEntityPersister) getDeclaringType();
|
|
||||||
if ( associationParentForeignKeyDescriptor.getTargetTableExpression()
|
|
||||||
.equals( entityPersister.getTableName() ) ) {
|
|
||||||
final String[] identifierColumnNames = entityPersister.getIdentifierColumnNames();
|
|
||||||
if ( associationParentForeignKeyDescriptor.areTargetColumnNamesEqualsTo( identifierColumnNames ) ) {
|
|
||||||
return createBiDirectionalFetch( fetchablePath, fetchParent );
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
while ( modelPart instanceof EmbeddableValuedFetchable ) {
|
||||||
|
parent = parent.getParent();
|
||||||
|
modelPart = creationState.resolveModelPart( parent );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.mappedBy != null && parent.getFullPath().endsWith( this.mappedBy ) ) {
|
||||||
|
/*
|
||||||
|
class Child {
|
||||||
|
@OneToOne(mappedBy = "biologicalChild")
|
||||||
|
private Mother mother;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Mother {
|
||||||
|
@OneToOne
|
||||||
|
private Child biologicalChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchablePath= Mother.biologicalChild.mother
|
||||||
|
this.mappedBy = "biologicalChild"
|
||||||
|
parent.getFullPath() = "Mother.biologicalChild"
|
||||||
|
*/
|
||||||
|
return createCircularBiDirectionalFetch(
|
||||||
|
fetchablePath,
|
||||||
|
fetchParent,
|
||||||
|
parent.getParent(),
|
||||||
|
LockMode.READ
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
check if mappedBy is on the other side of the association
|
||||||
|
*/
|
||||||
|
final String otherSideMappedBy = getOtherSideMappedBy( modelPart, parent.getParent(), creationState );
|
||||||
|
if ( otherSideMappedBy != null ) {
|
||||||
|
/*
|
||||||
|
class Child {
|
||||||
|
@OneToOne(mappedBy = "biologicalChild")
|
||||||
|
private Mother mother;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Mother {
|
||||||
|
@OneToOne
|
||||||
|
private Child biologicalChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchablePath = "Child.mother.biologicalChild"
|
||||||
|
otherSideAssociationModelPart = ToOneAttributeMapping("Child.mother")
|
||||||
|
otherSideMappedBy = "biologicalChild"
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( fetchablePath.getFullPath().endsWith( otherSideMappedBy ) ) {
|
||||||
|
return createCircularBiDirectionalFetch(
|
||||||
|
fetchablePath,
|
||||||
|
fetchParent,
|
||||||
|
parent.getParent(),
|
||||||
|
LockMode.READ
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
class Child {
|
||||||
|
@OneToOne
|
||||||
|
private Mother mother;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Mother {
|
||||||
|
@OneToOne
|
||||||
|
private Child stepMother;
|
||||||
|
}
|
||||||
|
|
||||||
|
We have a cirularity but it is not bidirectional
|
||||||
|
*/
|
||||||
|
if ( referringPrimaryKey ) {
|
||||||
|
final TableGroup parentTableGroup = creationState
|
||||||
|
.getSqlAstCreationState()
|
||||||
|
.getFromClauseAccess()
|
||||||
|
.getTableGroup( fetchParent.getNavigablePath() );
|
||||||
|
return new CircularFetchImpl(
|
||||||
|
getEntityMappingType(),
|
||||||
|
getTiming(),
|
||||||
|
fetchablePath,
|
||||||
|
fetchParent,
|
||||||
|
this,
|
||||||
|
fetchablePath,
|
||||||
|
foreignKeyDescriptor.createDomainResult( fetchablePath, parentTableGroup, creationState )
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Fetch createBiDirectionalFetch(NavigablePath fetchablePath, FetchParent fetchParent) {
|
private String getOtherSideMappedBy(
|
||||||
final EntityResultGraphNode referencedEntityReference = resolveEntityGraphNode( fetchParent );
|
ModelPart modelPart,
|
||||||
|
NavigablePath parentOfParent,
|
||||||
if ( referencedEntityReference == null ) {
|
DomainResultCreationState creationState) {
|
||||||
throw new HibernateException(
|
if ( modelPart instanceof ToOneAttributeMapping ) {
|
||||||
"Could not locate entity-valued reference for circular path `" + fetchablePath + "`"
|
return ( (ToOneAttributeMapping) modelPart ).getMappedBy();
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new BiDirectionalFetchImpl(
|
if ( modelPart instanceof EntityCollectionPart ) {
|
||||||
|
return ( (PluralAttributeMapping) creationState.resolveModelPart( parentOfParent ) ).getMappedBy();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMappedBy(){
|
||||||
|
return mappedBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Fetch createCircularBiDirectionalFetch(
|
||||||
|
NavigablePath fetchablePath,
|
||||||
|
FetchParent fetchParent,
|
||||||
|
NavigablePath referencedNavigablePath,
|
||||||
|
LockMode lockMode) {
|
||||||
|
return new CircularBiDirectionalFetchImpl(
|
||||||
FetchTiming.IMMEDIATE,
|
FetchTiming.IMMEDIATE,
|
||||||
fetchablePath,
|
fetchablePath,
|
||||||
fetchParent,
|
fetchParent,
|
||||||
this,
|
this,
|
||||||
referencedEntityReference.getNavigablePath()
|
lockMode,
|
||||||
|
referencedNavigablePath
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected EntityResultGraphNode resolveEntityGraphNode(FetchParent fetchParent) {
|
|
||||||
FetchParent processingParent = fetchParent;
|
|
||||||
while ( processingParent != null ) {
|
|
||||||
if ( processingParent instanceof EntityResultGraphNode ) {
|
|
||||||
return (EntityResultGraphNode) processingParent;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( processingParent instanceof Fetch ) {
|
|
||||||
processingParent = ( (Fetch) processingParent ).getFetchParent();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
processingParent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EntityFetch generateFetch(
|
public EntityFetch generateFetch(
|
||||||
FetchParent fetchParent,
|
FetchParent fetchParent,
|
||||||
|
@ -273,6 +323,7 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
||||||
LockMode lockMode,
|
LockMode lockMode,
|
||||||
String resultVariable,
|
String resultVariable,
|
||||||
DomainResultCreationState creationState) {
|
DomainResultCreationState creationState) {
|
||||||
|
|
||||||
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
|
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
|
||||||
final FromClauseAccess fromClauseAccess = sqlAstCreationState.getFromClauseAccess();
|
final FromClauseAccess fromClauseAccess = sqlAstCreationState.getFromClauseAccess();
|
||||||
|
|
||||||
|
@ -292,7 +343,6 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
||||||
sqlAstJoinType = SqlAstJoinType.INNER;
|
sqlAstJoinType = SqlAstJoinType.INNER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final TableGroupJoin tableGroupJoin = createTableGroupJoin(
|
final TableGroupJoin tableGroupJoin = createTableGroupJoin(
|
||||||
fetchablePath,
|
fetchablePath,
|
||||||
parentTableGroup,
|
parentTableGroup,
|
||||||
|
@ -306,6 +356,7 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
creationState.registerVisitedAssociationKey( foreignKeyDescriptor.getAssociationKey() );
|
||||||
return new EntityFetchJoinedImpl(
|
return new EntityFetchJoinedImpl(
|
||||||
fetchParent,
|
fetchParent,
|
||||||
this,
|
this,
|
||||||
|
@ -319,12 +370,41 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
||||||
//noinspection rawtypes
|
//noinspection rawtypes
|
||||||
final DomainResult keyResult;
|
final DomainResult keyResult;
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. No JoinTable
|
||||||
|
Model:
|
||||||
|
EntityA{
|
||||||
|
@ManyToOne
|
||||||
|
EntityB b
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityB{
|
||||||
|
@ManyToOne
|
||||||
|
EntityA a
|
||||||
|
}
|
||||||
|
|
||||||
|
Relational:
|
||||||
|
ENTITY_A( id )
|
||||||
|
ENTITY_B( id, entity_a_id)
|
||||||
|
|
||||||
|
1.1 EntityA -> EntityB : as keyResult we need ENTITY_B.id
|
||||||
|
1.2 EntityB -> EntityA : as keyResult we need ENTITY_B.entity_a_id (FK referring column)
|
||||||
|
|
||||||
|
2. JoinTable
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
boolean selectByUniqueKey;
|
||||||
if ( referringPrimaryKey ) {
|
if ( referringPrimaryKey ) {
|
||||||
|
// case 1.2
|
||||||
keyResult = foreignKeyDescriptor.createDomainResult( fetchablePath, parentTableGroup, creationState );
|
keyResult = foreignKeyDescriptor.createDomainResult( fetchablePath, parentTableGroup, creationState );
|
||||||
|
selectByUniqueKey = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
keyResult = ( (EntityPersister) getDeclaringType() ).getIdentifierMapping()
|
keyResult = ((EntityPersister) getDeclaringType()).getIdentifierMapping()
|
||||||
.createDomainResult( fetchablePath, parentTableGroup, null, creationState );
|
.createDomainResult( fetchablePath, parentTableGroup, null, creationState );
|
||||||
|
// case 1.1
|
||||||
|
selectByUniqueKey = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert !selected;
|
assert !selected;
|
||||||
|
@ -332,19 +412,17 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
||||||
return new EntityFetchSelectImpl(
|
return new EntityFetchSelectImpl(
|
||||||
fetchParent,
|
fetchParent,
|
||||||
this,
|
this,
|
||||||
lockMode,
|
|
||||||
isNullable,
|
isNullable,
|
||||||
fetchablePath,
|
fetchablePath,
|
||||||
keyResult,
|
keyResult,
|
||||||
|
selectByUniqueKey,
|
||||||
creationState
|
creationState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new EntityFetchDelayedImpl(
|
return new EntityDelayedFetchImpl(
|
||||||
fetchParent,
|
fetchParent,
|
||||||
this,
|
this,
|
||||||
lockMode,
|
|
||||||
isNullable,
|
|
||||||
fetchablePath,
|
fetchablePath,
|
||||||
keyResult
|
keyResult
|
||||||
);
|
);
|
||||||
|
|
|
@ -6347,10 +6347,10 @@ public abstract class AbstractEntityPersister
|
||||||
public void visitKeyFetchables(
|
public void visitKeyFetchables(
|
||||||
Consumer<Fetchable> fetchableConsumer,
|
Consumer<Fetchable> fetchableConsumer,
|
||||||
EntityMappingType treatTargetType) {
|
EntityMappingType treatTargetType) {
|
||||||
if ( getIdentifierMapping() instanceof FetchableContainer ) {
|
// if ( getIdentifierMapping() instanceof FetchableContainer ) {
|
||||||
// essentially means the entity has a composite id - ask the embeddable to visit its fetchables
|
// // essentially means the entity has a composite id - ask the embeddable to visit its fetchables
|
||||||
( (FetchableContainer) getIdentifierMapping() ).visitFetchables( fetchableConsumer, treatTargetType );
|
// ( (FetchableContainer) getIdentifierMapping() ).visitFetchables( fetchableConsumer, treatTargetType );
|
||||||
}
|
// }
|
||||||
// otherwise, nothing to do
|
// otherwise, nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ public interface UniqueKeyLoadable extends Loadable {
|
||||||
*/
|
*/
|
||||||
Object loadByUniqueKey(String propertyName, Object uniqueKey, SharedSessionContractImplementor session);
|
Object loadByUniqueKey(String propertyName, Object uniqueKey, SharedSessionContractImplementor session);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the property number of the unique key property
|
* Get the property number of the unique key property
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -10,6 +10,7 @@ import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
import org.hibernate.NotYetImplementedFor6Exception;
|
||||||
|
import org.hibernate.metamodel.mapping.AssociationKey;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.results.jdbc.spi.JdbcValues;
|
import org.hibernate.sql.results.jdbc.spi.JdbcValues;
|
||||||
|
@ -31,6 +32,13 @@ public interface DomainResultCreationState {
|
||||||
return (SqlAliasBaseManager) getSqlAstCreationState().getSqlAliasBaseGenerator();
|
return (SqlAliasBaseManager) getSqlAstCreationState().getSqlAliasBaseGenerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void registerVisitedAssociationKey(AssociationKey associationKey){
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isAssociationKeyVisited(AssociationKey associationKey){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the ModelPart associated with a given NavigablePath. More specific ModelParts should be preferred - e.g.
|
* Resolve the ModelPart associated with a given NavigablePath. More specific ModelParts should be preferred - e.g.
|
||||||
* the SingularAssociationAttributeMapping rather than just the EntityTypeMapping for the associated type
|
* the SingularAssociationAttributeMapping rather than just the EntityTypeMapping for the associated type
|
||||||
|
|
|
@ -29,7 +29,7 @@ import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||||
import org.hibernate.sql.results.graph.basic.BasicResult;
|
import org.hibernate.sql.results.graph.basic.BasicResult;
|
||||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
|
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
|
||||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
|
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
|
||||||
import org.hibernate.sql.results.graph.entity.internal.EntityFetchDelayedImpl;
|
import org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchImpl;
|
||||||
import org.hibernate.sql.results.graph.entity.internal.EntityFetchSelectImpl;
|
import org.hibernate.sql.results.graph.entity.internal.EntityFetchSelectImpl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -76,11 +76,9 @@ public class EmbeddableForeignKeyResultImpl<T>
|
||||||
);
|
);
|
||||||
Fetch fetch;
|
Fetch fetch;
|
||||||
if ( toOneAttributeMapping.getMappedFetchOptions().getTiming() == FetchTiming.DELAYED ) {
|
if ( toOneAttributeMapping.getMappedFetchOptions().getTiming() == FetchTiming.DELAYED ) {
|
||||||
fetch = new EntityFetchDelayedImpl(
|
fetch = new EntityDelayedFetchImpl(
|
||||||
this,
|
this,
|
||||||
toOneAttributeMapping,
|
toOneAttributeMapping,
|
||||||
null,
|
|
||||||
false,
|
|
||||||
navigablePath.append( fetchable.getFetchableName() ),
|
navigablePath.append( fetchable.getFetchableName() ),
|
||||||
domainResult
|
domainResult
|
||||||
);
|
);
|
||||||
|
@ -89,10 +87,10 @@ public class EmbeddableForeignKeyResultImpl<T>
|
||||||
fetch = new EntityFetchSelectImpl(
|
fetch = new EntityFetchSelectImpl(
|
||||||
this,
|
this,
|
||||||
toOneAttributeMapping,
|
toOneAttributeMapping,
|
||||||
null,
|
|
||||||
false,
|
false,
|
||||||
navigablePath.append( fetchable.getFetchableName() ),
|
navigablePath.append( fetchable.getFetchableName() ),
|
||||||
domainResult,
|
domainResult,
|
||||||
|
false,
|
||||||
creationState
|
creationState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -129,6 +127,11 @@ public class EmbeddableForeignKeyResultImpl<T>
|
||||||
return new EmbeddableAssembler( initializer );
|
return new EmbeddableAssembler( initializer );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NavigablePath getNavigablePath() {
|
||||||
|
return super.getNavigablePath().append( "{fk}");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EmbeddableMappingType getReferencedMappingType() {
|
public EmbeddableMappingType getReferencedMappingType() {
|
||||||
return (EmbeddableMappingType) getFetchContainer().getPartMappingType();
|
return (EmbeddableMappingType) getFetchContainer().getPartMappingType();
|
||||||
|
|
|
@ -121,7 +121,6 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
||||||
for ( int i = 0; i < identifierInitializers.size(); i++ ) {
|
for ( int i = 0; i < identifierInitializers.size(); i++ ) {
|
||||||
final Initializer existing = identifierInitializers.get( i );
|
final Initializer existing = identifierInitializers.get( i );
|
||||||
if ( existing.getNavigablePath().equals( navigablePath ) ) {
|
if ( existing.getNavigablePath().equals( navigablePath ) ) {
|
||||||
identifierInitializers.add( existing );
|
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,9 +327,24 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
protected void initializeIdentifier(RowProcessingState rowProcessingState) {
|
protected void initializeIdentifier(RowProcessingState rowProcessingState) {
|
||||||
|
if ( EntityLoadingLogger.TRACE_ENABLED ) {
|
||||||
|
EntityLoadingLogger.LOGGER.tracef(
|
||||||
|
"(%s) Beginning Initializer#initializeIdentifier process for entity (%s) ",
|
||||||
|
StringHelper.collapse( this.getClass().getName() ),
|
||||||
|
getNavigablePath()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
identifierInitializers.forEach( initializer -> initializer.resolveKey( rowProcessingState ) );
|
identifierInitializers.forEach( initializer -> initializer.resolveKey( rowProcessingState ) );
|
||||||
identifierInitializers.forEach( initializer -> initializer.resolveInstance( rowProcessingState ) );
|
identifierInitializers.forEach( initializer -> initializer.resolveInstance( rowProcessingState ) );
|
||||||
identifierInitializers.forEach( initializer -> initializer.initializeInstance( rowProcessingState ) );
|
|
||||||
|
if ( EntityLoadingLogger.TRACE_ENABLED ) {
|
||||||
|
EntityLoadingLogger.LOGGER.tracef(
|
||||||
|
"(%s) Fiish Initializer#initializeIdentifier process for entity (%s) ",
|
||||||
|
StringHelper.collapse( this.getClass().getName() ),
|
||||||
|
getNavigablePath()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
@ -351,8 +365,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
||||||
else {
|
else {
|
||||||
id = identifierAssembler.assemble(
|
id = identifierAssembler.assemble(
|
||||||
rowProcessingState,
|
rowProcessingState,
|
||||||
jdbcValuesSourceProcessingState
|
jdbcValuesSourceProcessingState.getProcessingOptions()
|
||||||
.getProcessingOptions()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,6 +377,10 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
||||||
// 2) build the EntityKey
|
// 2) build the EntityKey
|
||||||
this.entityKey = new EntityKey( id, concreteDescriptor );
|
this.entityKey = new EntityKey( id, concreteDescriptor );
|
||||||
|
|
||||||
|
if ( jdbcValuesSourceProcessingState.findInitializer( entityKey ) == null ) {
|
||||||
|
jdbcValuesSourceProcessingState.registerInitilaizer( entityKey, this );
|
||||||
|
}
|
||||||
|
|
||||||
// 3) schedule the EntityKey for batch loading, if possible
|
// 3) schedule the EntityKey for batch loading, if possible
|
||||||
if ( concreteDescriptor.isBatchLoadable() ) {
|
if ( concreteDescriptor.isBatchLoadable() ) {
|
||||||
if ( !session.getPersistenceContext().containsEntity( entityKey ) ) {
|
if ( !session.getPersistenceContext().containsEntity( entityKey ) ) {
|
||||||
|
@ -377,6 +394,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
||||||
if ( missing ) {
|
if ( missing ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
identifierInitializers.forEach( initializer -> initializer.initializeInstance( rowProcessingState ) );
|
||||||
|
|
||||||
final Object entityIdentifier = entityKey.getIdentifier();
|
final Object entityIdentifier = entityKey.getIdentifier();
|
||||||
|
|
||||||
|
@ -509,7 +527,6 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// todo (6.0): do we really need this check ?
|
// todo (6.0): do we really need this check ?
|
||||||
if ( persistenceContext.containsEntity( entityKey ) ) {
|
if ( persistenceContext.containsEntity( entityKey ) ) {
|
||||||
Status status = persistenceContext.getEntry( persistenceContext.getEntity( entityKey ) )
|
Status status = persistenceContext.getEntry( persistenceContext.getEntity( entityKey ) )
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.sql.results.graph.entity;
|
package org.hibernate.sql.results.graph.entity;
|
||||||
|
|
||||||
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
import org.hibernate.engine.spi.EntityKey;
|
||||||
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;
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.sql.results.graph.entity.internal;
|
package org.hibernate.sql.results.graph.entity.internal;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchTiming;
|
||||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
|
@ -21,23 +20,16 @@ import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||||
* @author Andrea Boriero
|
* @author Andrea Boriero
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class EntityFetchDelayedImpl extends AbstractNonJoinedEntityFetch {
|
public class EntityDelayedFetchImpl extends AbstractNonJoinedEntityFetch {
|
||||||
private final LockMode lockMode;
|
|
||||||
private final boolean nullable;
|
|
||||||
|
|
||||||
private final DomainResult keyResult;
|
private final DomainResult keyResult;
|
||||||
|
|
||||||
public EntityFetchDelayedImpl(
|
public EntityDelayedFetchImpl(
|
||||||
FetchParent fetchParent,
|
FetchParent fetchParent,
|
||||||
ToOneAttributeMapping fetchedAttribute,
|
ToOneAttributeMapping fetchedAttribute,
|
||||||
LockMode lockMode,
|
|
||||||
boolean nullable,
|
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
DomainResult keyResult) {
|
DomainResult keyResult) {
|
||||||
super( navigablePath, fetchedAttribute, fetchParent );
|
super( navigablePath, fetchedAttribute, fetchParent );
|
||||||
this.lockMode = lockMode;
|
|
||||||
this.nullable = nullable;
|
|
||||||
|
|
||||||
this.keyResult = keyResult;
|
this.keyResult = keyResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +49,7 @@ public class EntityFetchDelayedImpl extends AbstractNonJoinedEntityFetch {
|
||||||
AssemblerCreationState creationState) {
|
AssemblerCreationState creationState) {
|
||||||
final EntityInitializer entityInitializer = (EntityInitializer) creationState.resolveInitializer(
|
final EntityInitializer entityInitializer = (EntityInitializer) creationState.resolveInitializer(
|
||||||
getNavigablePath(),
|
getNavigablePath(),
|
||||||
() -> new EntityFetchDelayedInitializer(
|
() -> new EntityDelayedFetchInitializer(
|
||||||
getNavigablePath(),
|
getNavigablePath(),
|
||||||
getEntityValuedModelPart().getEntityMappingType().getEntityPersister(),
|
getEntityValuedModelPart().getEntityMappingType().getEntityPersister(),
|
||||||
keyResult.createResultAssembler( creationState )
|
keyResult.createResultAssembler( creationState )
|
|
@ -11,6 +11,7 @@ import java.util.function.Consumer;
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
import org.hibernate.NotYetImplementedFor6Exception;
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
import org.hibernate.engine.spi.EntityKey;
|
||||||
import org.hibernate.engine.spi.PersistenceContext;
|
import org.hibernate.engine.spi.PersistenceContext;
|
||||||
|
import org.hibernate.internal.log.LoggingHelper;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.results.graph.AbstractFetchParentAccess;
|
import org.hibernate.sql.results.graph.AbstractFetchParentAccess;
|
||||||
|
@ -23,7 +24,7 @@ import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||||
* @author Andrea Boriero
|
* @author Andrea Boriero
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class EntityFetchDelayedInitializer extends AbstractFetchParentAccess implements EntityInitializer {
|
public class EntityDelayedFetchInitializer extends AbstractFetchParentAccess implements EntityInitializer {
|
||||||
|
|
||||||
private final NavigablePath navigablePath;
|
private final NavigablePath navigablePath;
|
||||||
private final EntityPersister concreteDescriptor;
|
private final EntityPersister concreteDescriptor;
|
||||||
|
@ -32,7 +33,7 @@ public class EntityFetchDelayedInitializer extends AbstractFetchParentAccess imp
|
||||||
private Object entityInstance;
|
private Object entityInstance;
|
||||||
private Object identifier;
|
private Object identifier;
|
||||||
|
|
||||||
protected EntityFetchDelayedInitializer(
|
public EntityDelayedFetchInitializer(
|
||||||
NavigablePath fetchedNavigable,
|
NavigablePath fetchedNavigable,
|
||||||
EntityPersister concreteDescriptor,
|
EntityPersister concreteDescriptor,
|
||||||
DomainResultAssembler identifierAssembler) {
|
DomainResultAssembler identifierAssembler) {
|
||||||
|
@ -147,4 +148,9 @@ public class EntityFetchDelayedInitializer extends AbstractFetchParentAccess imp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "EntityDelayedFetchInitializer(" + LoggingHelper.toLoggableString( navigablePath ) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -6,9 +6,9 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.sql.results.graph.entity.internal;
|
package org.hibernate.sql.results.graph.entity.internal;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchTiming;
|
||||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
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.DomainResult;
|
||||||
|
@ -26,18 +26,20 @@ import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||||
public class EntityFetchSelectImpl extends AbstractNonJoinedEntityFetch {
|
public class EntityFetchSelectImpl extends AbstractNonJoinedEntityFetch {
|
||||||
private final boolean nullable;
|
private final boolean nullable;
|
||||||
private final DomainResult result;
|
private final DomainResult result;
|
||||||
|
private final boolean selectByUniqueKey;
|
||||||
|
|
||||||
public EntityFetchSelectImpl(
|
public EntityFetchSelectImpl(
|
||||||
FetchParent fetchParent,
|
FetchParent fetchParent,
|
||||||
ToOneAttributeMapping fetchedAttribute,
|
ToOneAttributeMapping fetchedAttribute,
|
||||||
LockMode lockMode,
|
|
||||||
boolean nullable,
|
boolean nullable,
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
DomainResult result,
|
DomainResult result,
|
||||||
|
boolean selectByUniqueKey,
|
||||||
DomainResultCreationState creationState) {
|
DomainResultCreationState creationState) {
|
||||||
super( navigablePath, fetchedAttribute, fetchParent );
|
super( navigablePath, fetchedAttribute, fetchParent );
|
||||||
this.nullable = nullable;
|
this.nullable = nullable;
|
||||||
this.result = result;
|
this.result = result;
|
||||||
|
this.selectByUniqueKey = selectByUniqueKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -54,12 +56,26 @@ public class EntityFetchSelectImpl extends AbstractNonJoinedEntityFetch {
|
||||||
public DomainResultAssembler createAssembler(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
public DomainResultAssembler createAssembler(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||||
final EntityInitializer initializer = (EntityInitializer) creationState.resolveInitializer(
|
final EntityInitializer initializer = (EntityInitializer) creationState.resolveInitializer(
|
||||||
getNavigablePath(),
|
getNavigablePath(),
|
||||||
() -> new EntitySelectFetchInitializer(
|
() -> {
|
||||||
getNavigablePath(),
|
|
||||||
getReferencedMappingContainer().getEntityPersister(),
|
EntityPersister entityPersister = getReferencedMappingContainer().getEntityPersister();
|
||||||
result.createResultAssembler( creationState ),
|
|
||||||
nullable
|
if ( selectByUniqueKey ) {
|
||||||
)
|
return new EntitySelectFetchByUniqueKeyInitializer(
|
||||||
|
(ToOneAttributeMapping) getFetchedMapping(),
|
||||||
|
getNavigablePath(),
|
||||||
|
entityPersister,
|
||||||
|
result.createResultAssembler( creationState ),
|
||||||
|
nullable
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return new EntitySelectFetchInitializer(
|
||||||
|
getNavigablePath(),
|
||||||
|
entityPersister,
|
||||||
|
result.createResultAssembler( creationState ),
|
||||||
|
nullable
|
||||||
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return new EntityAssembler( getResultJavaTypeDescriptor(), initializer );
|
return new EntityAssembler( getResultJavaTypeDescriptor(), initializer );
|
||||||
|
|
|
@ -55,6 +55,6 @@ public class EntityResultInitializer extends AbstractEntityInitializer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "EntityRootInitializer(" + getNavigablePath().getFullPath() + ")";
|
return CONCRETE_NAME + "(" + getNavigablePath().getFullPath() + ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.sql.results.graph.entity.internal;
|
||||||
|
|
||||||
|
import org.hibernate.engine.spi.EntityUniqueKey;
|
||||||
|
import org.hibernate.engine.spi.PersistenceContext;
|
||||||
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.persister.entity.UniqueKeyLoadable;
|
||||||
|
import org.hibernate.query.NavigablePath;
|
||||||
|
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||||
|
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Andrea Boriero
|
||||||
|
*/
|
||||||
|
public class EntitySelectFetchByUniqueKeyInitializer extends EntitySelectFetchInitializer {
|
||||||
|
private final ToOneAttributeMapping fetchedAttribute;
|
||||||
|
|
||||||
|
public EntitySelectFetchByUniqueKeyInitializer(
|
||||||
|
ToOneAttributeMapping fetchedAttribute,
|
||||||
|
NavigablePath fetchedNavigable,
|
||||||
|
EntityPersister concreteDescriptor,
|
||||||
|
DomainResultAssembler identifierAssembler,
|
||||||
|
boolean nullable) {
|
||||||
|
super( fetchedNavigable, concreteDescriptor, identifierAssembler, nullable );
|
||||||
|
this.fetchedAttribute = fetchedAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeInstance(RowProcessingState rowProcessingState) {
|
||||||
|
if ( entityInstance != null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Object entityIdentifier = identifierAssembler.assemble( rowProcessingState );
|
||||||
|
if ( entityIdentifier == null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String entityName = concreteDescriptor.getEntityName();
|
||||||
|
String uniqueKeyPropertyName = fetchedAttribute.getMappedBy();
|
||||||
|
|
||||||
|
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
||||||
|
|
||||||
|
EntityUniqueKey euk = new EntityUniqueKey(
|
||||||
|
entityName,
|
||||||
|
uniqueKeyPropertyName,
|
||||||
|
entityIdentifier,
|
||||||
|
concreteDescriptor.getIdentifierType(),
|
||||||
|
concreteDescriptor.getEntityMode(),
|
||||||
|
session.getFactory()
|
||||||
|
);
|
||||||
|
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||||
|
entityInstance = persistenceContext.getEntity( euk );
|
||||||
|
if ( entityInstance == null ) {
|
||||||
|
entityInstance = ( (UniqueKeyLoadable) concreteDescriptor ).loadByUniqueKey(
|
||||||
|
uniqueKeyPropertyName,
|
||||||
|
entityIdentifier,
|
||||||
|
session
|
||||||
|
);
|
||||||
|
|
||||||
|
// If the entity was not in the Persistence Context, but was found now,
|
||||||
|
// add it to the Persistence Context
|
||||||
|
if ( entityInstance != null ) {
|
||||||
|
persistenceContext.addEntity( euk, entityInstance );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( entityInstance != null ) {
|
||||||
|
entityInstance = persistenceContext.proxyFor( entityInstance );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,28 +10,39 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
import org.hibernate.NotYetImplementedFor6Exception;
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
import org.hibernate.engine.spi.EntityKey;
|
||||||
|
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.util.StringHelper;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.proxy.HibernateProxy;
|
import org.hibernate.proxy.HibernateProxy;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.results.graph.AbstractFetchParentAccess;
|
import org.hibernate.sql.results.graph.AbstractFetchParentAccess;
|
||||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||||
|
import org.hibernate.sql.results.graph.Initializer;
|
||||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||||
|
import org.hibernate.sql.results.graph.entity.EntityLoadingLogger;
|
||||||
|
import org.hibernate.sql.results.graph.entity.EntityValuedFetchable;
|
||||||
|
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
|
||||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||||
|
|
||||||
|
import static org.hibernate.internal.log.LoggingHelper.toLoggableString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Andrea Boriero
|
* @author Andrea Boriero
|
||||||
*/
|
*/
|
||||||
public class EntitySelectFetchInitializer extends AbstractFetchParentAccess implements EntityInitializer {
|
public class EntitySelectFetchInitializer extends AbstractFetchParentAccess implements EntityInitializer {
|
||||||
|
private static final String CONCRETE_NAME = EntitySelectFetchInitializer.class.getSimpleName();
|
||||||
|
|
||||||
private final NavigablePath navigablePath;
|
private final NavigablePath navigablePath;
|
||||||
private final EntityPersister concreteDescriptor;
|
|
||||||
private final DomainResultAssembler identifierAssembler;
|
|
||||||
private final boolean isEnhancedForLazyLoading;
|
private final boolean isEnhancedForLazyLoading;
|
||||||
private final boolean nullable;
|
private final boolean nullable;
|
||||||
|
|
||||||
private Object entityInstance;
|
protected final EntityPersister concreteDescriptor;
|
||||||
|
protected final DomainResultAssembler identifierAssembler;
|
||||||
|
protected Object entityInstance;
|
||||||
|
|
||||||
protected EntitySelectFetchInitializer(
|
public EntitySelectFetchInitializer(
|
||||||
NavigablePath fetchedNavigable,
|
NavigablePath fetchedNavigable,
|
||||||
EntityPersister concreteDescriptor,
|
EntityPersister concreteDescriptor,
|
||||||
DomainResultAssembler identifierAssembler,
|
DomainResultAssembler identifierAssembler,
|
||||||
|
@ -55,7 +66,6 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -64,21 +74,106 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Object id = identifierAssembler.assemble( rowProcessingState );
|
final Object entityIdentifier = identifierAssembler.assemble( rowProcessingState );
|
||||||
if ( id == null ) {
|
|
||||||
|
if ( entityIdentifier == null ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String entityName = concreteDescriptor.getEntityName();
|
if ( EntityLoadingLogger.TRACE_ENABLED ) {
|
||||||
|
EntityLoadingLogger.LOGGER.tracef(
|
||||||
|
"(%s) Beginning Initializer#resolveInstance process for entity (%s) : %s",
|
||||||
|
StringHelper.collapse( this.getClass().getName() ),
|
||||||
|
getNavigablePath(),
|
||||||
|
entityIdentifier
|
||||||
|
);
|
||||||
|
}
|
||||||
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
||||||
|
final String entityName = concreteDescriptor.getEntityName();
|
||||||
|
|
||||||
|
final EntityKey entityKey = new EntityKey( entityIdentifier, concreteDescriptor );
|
||||||
|
|
||||||
|
Initializer initializer = rowProcessingState.getJdbcValuesSourceProcessingState().findInitializer(
|
||||||
|
entityKey );
|
||||||
|
|
||||||
|
if ( initializer != null ) {
|
||||||
|
if ( EntityLoadingLogger.DEBUG_ENABLED ) {
|
||||||
|
EntityLoadingLogger.LOGGER.debugf(
|
||||||
|
"(%s) Found an initializer for entity (%s) : %s",
|
||||||
|
CONCRETE_NAME,
|
||||||
|
toLoggableString( getNavigablePath(), entityIdentifier ),
|
||||||
|
entityIdentifier
|
||||||
|
);
|
||||||
|
}
|
||||||
|
initializer.resolveInstance( rowProcessingState );
|
||||||
|
entityInstance = initializer.getInitializedInstance();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||||
|
entityInstance = persistenceContext.getEntity( entityKey );
|
||||||
|
if ( entityInstance != null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final LoadingEntityEntry existingLoadingEntry = session
|
||||||
|
.getPersistenceContext()
|
||||||
|
.getLoadContexts()
|
||||||
|
.findLoadingEntityEntry( entityKey );
|
||||||
|
|
||||||
|
if ( existingLoadingEntry != null ) {
|
||||||
|
if ( EntityLoadingLogger.DEBUG_ENABLED ) {
|
||||||
|
EntityLoadingLogger.LOGGER.debugf(
|
||||||
|
"(%s) Found existing loading entry [%s] - using loading instance",
|
||||||
|
CONCRETE_NAME,
|
||||||
|
toLoggableString(
|
||||||
|
getNavigablePath(),
|
||||||
|
entityIdentifier
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.entityInstance = existingLoadingEntry.getEntityInstance();
|
||||||
|
|
||||||
|
if ( existingLoadingEntry.getEntityInitializer() != this ) {
|
||||||
|
// the entity is already being loaded elsewhere
|
||||||
|
if ( EntityLoadingLogger.DEBUG_ENABLED ) {
|
||||||
|
EntityLoadingLogger.LOGGER.debugf(
|
||||||
|
"(%s) Entity [%s] being loaded by another initializer [%s] - skipping processing",
|
||||||
|
CONCRETE_NAME,
|
||||||
|
toLoggableString( getNavigablePath(), entityIdentifier ),
|
||||||
|
existingLoadingEntry.getEntityInitializer()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// EARLY EXIT!!!
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( EntityLoadingLogger.DEBUG_ENABLED ) {
|
||||||
|
EntityLoadingLogger.LOGGER.debugf(
|
||||||
|
"(%s) Invoking session#internalLoad for entity (%s) : %s",
|
||||||
|
CONCRETE_NAME,
|
||||||
|
toLoggableString( getNavigablePath(), entityIdentifier ),
|
||||||
|
entityIdentifier
|
||||||
|
);
|
||||||
|
}
|
||||||
entityInstance = session.internalLoad(
|
entityInstance = session.internalLoad(
|
||||||
entityName,
|
entityName,
|
||||||
id,
|
entityIdentifier,
|
||||||
true,
|
true,
|
||||||
nullable
|
nullable
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ( EntityLoadingLogger.DEBUG_ENABLED ) {
|
||||||
|
EntityLoadingLogger.LOGGER.debugf(
|
||||||
|
"(%s) Entity [%s] : %s has being loaded by session.internalLoad.",
|
||||||
|
CONCRETE_NAME,
|
||||||
|
toLoggableString( getNavigablePath(), entityIdentifier ),
|
||||||
|
entityIdentifier
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if ( entityInstance instanceof HibernateProxy && isEnhancedForLazyLoading ) {
|
if ( entityInstance instanceof HibernateProxy && isEnhancedForLazyLoading ) {
|
||||||
( (HibernateProxy) entityInstance ).getHibernateLazyInitializer().setUnwrap( true );
|
( (HibernateProxy) entityInstance ).getHibernateLazyInitializer().setUnwrap( true );
|
||||||
}
|
}
|
||||||
|
@ -120,4 +215,9 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
|
||||||
super.registerResolutionListener( listener );
|
super.registerResolutionListener( listener );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "EntitySelectFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,213 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.sql.results.graph.entity.internal;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.hibernate.LockMode;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityVersionMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
||||||
|
import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||||
|
import org.hibernate.query.NavigablePath;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
import org.hibernate.sql.results.graph.AbstractFetchParent;
|
||||||
|
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.DomainResultCreationState;
|
||||||
|
import org.hibernate.sql.results.graph.FetchableContainer;
|
||||||
|
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||||
|
import org.hibernate.sql.results.graph.entity.EntityResult;
|
||||||
|
import org.hibernate.sql.results.graph.entity.EntityResultGraphNode;
|
||||||
|
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Andrea Boriero
|
||||||
|
*/
|
||||||
|
public class RootEntityResultImpl extends AbstractFetchParent implements EntityResultGraphNode, EntityResult {
|
||||||
|
|
||||||
|
private final String resultVariable;
|
||||||
|
|
||||||
|
private final EntityValuedModelPart referencedModelPart;
|
||||||
|
private final DomainResult discriminatorResult;
|
||||||
|
private final DomainResult versionResult;
|
||||||
|
private DomainResult identifierResult;
|
||||||
|
private final LockMode lockMode;
|
||||||
|
|
||||||
|
public RootEntityResultImpl(
|
||||||
|
NavigablePath navigablePath,
|
||||||
|
EntityValuedModelPart entityValuedModelPart,
|
||||||
|
String resultVariable,
|
||||||
|
DomainResultCreationState creationState) {
|
||||||
|
this( navigablePath, entityValuedModelPart, resultVariable, null, creationState );
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public RootEntityResultImpl(
|
||||||
|
NavigablePath navigablePath,
|
||||||
|
EntityValuedModelPart entityValuedModelPart,
|
||||||
|
String resultVariable,
|
||||||
|
EntityMappingType targetType,
|
||||||
|
DomainResultCreationState creationState) {
|
||||||
|
super( entityValuedModelPart.getEntityMappingType(), navigablePath );
|
||||||
|
this.resultVariable = resultVariable;
|
||||||
|
this.referencedModelPart = entityValuedModelPart;
|
||||||
|
this.lockMode = creationState.getSqlAstCreationState().determineLockMode( resultVariable );
|
||||||
|
|
||||||
|
final EntityMappingType entityDescriptor = referencedModelPart.getEntityMappingType();
|
||||||
|
|
||||||
|
final TableGroup entityTableGroup = creationState.getSqlAstCreationState().getFromClauseAccess().findTableGroup( navigablePath );
|
||||||
|
|
||||||
|
EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping();
|
||||||
|
if ( identifierMapping instanceof SingleAttributeIdentifierMapping ) {
|
||||||
|
identifierMapping.createDomainResult(
|
||||||
|
navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ),
|
||||||
|
entityTableGroup,
|
||||||
|
null,
|
||||||
|
creationState
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
visitCompositeIdentifierMapping( navigablePath, creationState, identifierMapping, entityTableGroup );
|
||||||
|
}
|
||||||
|
|
||||||
|
final EntityDiscriminatorMapping discriminatorMapping = getDiscriminatorMapping( entityDescriptor, entityTableGroup );
|
||||||
|
if ( discriminatorMapping != null ) {
|
||||||
|
discriminatorResult = discriminatorMapping.createDomainResult(
|
||||||
|
navigablePath.append( EntityDiscriminatorMapping.ROLE_NAME ),
|
||||||
|
entityTableGroup,
|
||||||
|
null,
|
||||||
|
creationState
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
discriminatorResult = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final EntityVersionMapping versionDescriptor = entityDescriptor.getVersionMapping();
|
||||||
|
if ( versionDescriptor == null ) {
|
||||||
|
versionResult = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
versionResult = versionDescriptor.createDomainResult(
|
||||||
|
navigablePath.append( versionDescriptor.getFetchableName() ),
|
||||||
|
entityTableGroup,
|
||||||
|
null,
|
||||||
|
creationState
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
afterInitialize( creationState );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void visitCompositeIdentifierMapping(
|
||||||
|
NavigablePath navigablePath,
|
||||||
|
DomainResultCreationState creationState,
|
||||||
|
EntityIdentifierMapping identifierMapping,
|
||||||
|
TableGroup entityTableGroup) {
|
||||||
|
ManagedMappingType mappingType = (ManagedMappingType) identifierMapping.getPartMappingType();
|
||||||
|
fetches = new ArrayList<>();
|
||||||
|
mappingType.visitAttributeMappings(
|
||||||
|
attributeMapping -> {
|
||||||
|
if ( attributeMapping instanceof ToOneAttributeMapping ) {
|
||||||
|
((ToOneAttributeMapping)attributeMapping).getForeignKeyDescriptor().createDomainResult(
|
||||||
|
navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ),
|
||||||
|
entityTableGroup,
|
||||||
|
null,
|
||||||
|
creationState
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
attributeMapping.createDomainResult(
|
||||||
|
navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ),
|
||||||
|
entityTableGroup,
|
||||||
|
null,
|
||||||
|
creationState
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected EntityDiscriminatorMapping getDiscriminatorMapping(
|
||||||
|
EntityMappingType entityDescriptor,
|
||||||
|
TableGroup entityTableGroup) {
|
||||||
|
return entityDescriptor.getDiscriminatorMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityMappingType getReferencedMappingContainer() {
|
||||||
|
return getEntityValuedModelPart().getEntityMappingType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityValuedModelPart getEntityValuedModelPart() {
|
||||||
|
return referencedModelPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaTypeDescriptor getResultJavaTypeDescriptor() {
|
||||||
|
return getEntityValuedModelPart().getEntityMappingType().getMappedJavaTypeDescriptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockMode getLockMode() {
|
||||||
|
return lockMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DomainResult getDiscriminatorResult() {
|
||||||
|
return discriminatorResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DomainResult getVersionResult() {
|
||||||
|
return versionResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FetchableContainer getReferencedMappingType() {
|
||||||
|
return getReferencedMappingContainer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityValuedModelPart getReferencedModePart() {
|
||||||
|
return getEntityValuedModelPart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getResultVariable() {
|
||||||
|
return resultVariable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DomainResultAssembler createResultAssembler(AssemblerCreationState creationState) {
|
||||||
|
// todo (6.0) : seems like here is where we ought to determine the SQL selection mappings
|
||||||
|
|
||||||
|
final EntityInitializer initializer = (EntityInitializer) creationState.resolveInitializer(
|
||||||
|
getNavigablePath(),
|
||||||
|
() -> new EntityResultInitializer(
|
||||||
|
this,
|
||||||
|
getNavigablePath(),
|
||||||
|
getLockMode(),
|
||||||
|
identifierResult,
|
||||||
|
getDiscriminatorResult(),
|
||||||
|
getVersionResult(),
|
||||||
|
creationState
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return new EntityAssembler( getResultJavaTypeDescriptor(), initializer );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "EntityResultImpl {" + getNavigablePath() + "}";
|
||||||
|
}
|
||||||
|
}
|
|
@ -138,25 +138,27 @@ public class StandardRowReader<T> implements RowReader<T> {
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// old
|
// old
|
||||||
|
|
||||||
for ( int i = 0; i < initializers.size(); i++ ) {
|
final int numberOfInitializers = initializers.size();
|
||||||
|
|
||||||
|
for ( int i = 0; i < numberOfInitializers; i++ ) {
|
||||||
final Initializer initializer = initializers.get( i );
|
final Initializer initializer = initializers.get( i );
|
||||||
if ( ! ( initializer instanceof CollectionInitializer ) ) {
|
if ( ! ( initializer instanceof CollectionInitializer ) ) {
|
||||||
initializer.resolveKey( rowProcessingState );
|
initializer.resolveKey( rowProcessingState );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( int i = 0; i < initializers.size(); i++ ) {
|
for ( int i = 0; i < numberOfInitializers; i++ ) {
|
||||||
final Initializer initializer = initializers.get( i );
|
final Initializer initializer = initializers.get( i );
|
||||||
if ( initializer instanceof CollectionInitializer ) {
|
if ( initializer instanceof CollectionInitializer ) {
|
||||||
initializer.resolveKey( rowProcessingState );
|
initializer.resolveKey( rowProcessingState );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( int i = 0; i < initializers.size(); i++ ) {
|
for ( int i = 0; i < numberOfInitializers; i++ ) {
|
||||||
initializers.get( i ).resolveInstance( rowProcessingState );
|
initializers.get( i ).resolveInstance( rowProcessingState );
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( int i = 0; i < initializers.size(); i++ ) {
|
for ( int i = 0; i < numberOfInitializers; i++ ) {
|
||||||
initializers.get( i ).initializeInstance( rowProcessingState );
|
initializers.get( i ).initializeInstance( rowProcessingState );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,24 +39,27 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||||
/**
|
/**
|
||||||
* @author Andrea Boriero
|
* @author Andrea Boriero
|
||||||
*/
|
*/
|
||||||
public class BiDirectionalFetchImpl implements BiDirectionalFetch, Association {
|
public class CircularBiDirectionalFetchImpl implements BiDirectionalFetch, Association {
|
||||||
private final FetchTiming timing;
|
private final FetchTiming timing;
|
||||||
private final NavigablePath navigablePath;
|
private final NavigablePath navigablePath;
|
||||||
private final Fetchable fetchable;
|
private final Fetchable fetchable;
|
||||||
|
|
||||||
private final FetchParent fetchParent;
|
private final FetchParent fetchParent;
|
||||||
|
private final LockMode lockMode;
|
||||||
private final NavigablePath referencedNavigablePath;
|
private final NavigablePath referencedNavigablePath;
|
||||||
|
|
||||||
public BiDirectionalFetchImpl(
|
public CircularBiDirectionalFetchImpl(
|
||||||
FetchTiming timing,
|
FetchTiming timing,
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
FetchParent fetchParent,
|
FetchParent fetchParent,
|
||||||
Fetchable fetchable,
|
Fetchable fetchable,
|
||||||
|
LockMode lockMode,
|
||||||
NavigablePath referencedNavigablePath) {
|
NavigablePath referencedNavigablePath) {
|
||||||
this.timing = timing;
|
this.timing = timing;
|
||||||
this.fetchParent = fetchParent;
|
this.fetchParent = fetchParent;
|
||||||
this.navigablePath = navigablePath;
|
this.navigablePath = navigablePath;
|
||||||
this.fetchable = fetchable;
|
this.fetchable = fetchable;
|
||||||
|
this.lockMode = lockMode;
|
||||||
this.referencedNavigablePath = referencedNavigablePath;
|
this.referencedNavigablePath = referencedNavigablePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,24 +177,25 @@ public class BiDirectionalFetchImpl implements BiDirectionalFetch, Association {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object assemble(RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) {
|
public Object assemble(RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) {
|
||||||
final EntityInitializer initializer = resolveCircularInitializer( rowProcessingState );
|
EntityInitializer initializer = resolveCircularInitializer( rowProcessingState );
|
||||||
if ( initializer == null ) {
|
if ( initializer == null ) {
|
||||||
|
final Initializer parentInitializer = rowProcessingState.resolveInitializer( circularPath );
|
||||||
|
if ( circularPath.getParent() != null ) {
|
||||||
|
initializer = (EntityInitializer) rowProcessingState.resolveInitializer( circularPath.getParent() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert parentInitializer instanceof CollectionInitializer;
|
||||||
|
final CollectionInitializer circ = (CollectionInitializer) parentInitializer;
|
||||||
|
final CollectionKey collectionKey = circ.resolveCollectionKey( rowProcessingState );
|
||||||
|
final EntityKey entityKey = new EntityKey(
|
||||||
|
collectionKey.getKey(),
|
||||||
|
(EntityPersister) ( (AttributeMapping) fetchable ).getMappedTypeDescriptor()
|
||||||
|
);
|
||||||
|
|
||||||
final Initializer parentInitializer = rowProcessingState.resolveInitializer(
|
final SharedSessionContractImplementor session = rowProcessingState.getJdbcValuesSourceProcessingState()
|
||||||
circularPath.getParent() );
|
.getSession();
|
||||||
assert parentInitializer instanceof CollectionInitializer;
|
return session.getPersistenceContext().getEntity( entityKey );
|
||||||
final CollectionInitializer circ = (CollectionInitializer) parentInitializer;
|
}
|
||||||
final CollectionKey collectionKey = circ.resolveCollectionKey( rowProcessingState );
|
|
||||||
final EntityKey entityKey = new EntityKey(
|
|
||||||
collectionKey.getKey(),
|
|
||||||
(EntityPersister) ( (AttributeMapping) fetchable ).getMappedTypeDescriptor()
|
|
||||||
);
|
|
||||||
|
|
||||||
final SharedSessionContractImplementor session = rowProcessingState.getJdbcValuesSourceProcessingState()
|
|
||||||
.getSession();
|
|
||||||
return session.getPersistenceContext()
|
|
||||||
.getEntity( entityKey );
|
|
||||||
|
|
||||||
}
|
}
|
||||||
if ( initializer.getInitializedInstance() == null ) {
|
if ( initializer.getInitializedInstance() == null ) {
|
||||||
initializer.resolveKey( rowProcessingState );
|
initializer.resolveKey( rowProcessingState );
|
||||||
|
@ -203,6 +207,12 @@ public class BiDirectionalFetchImpl implements BiDirectionalFetch, Association {
|
||||||
|
|
||||||
private EntityInitializer resolveCircularInitializer(RowProcessingState rowProcessingState) {
|
private EntityInitializer resolveCircularInitializer(RowProcessingState rowProcessingState) {
|
||||||
final Initializer initializer = rowProcessingState.resolveInitializer( circularPath );
|
final Initializer initializer = rowProcessingState.resolveInitializer( circularPath );
|
||||||
|
if ( initializer instanceof EntityInitializer ) {
|
||||||
|
return (EntityInitializer) initializer;
|
||||||
|
}
|
||||||
|
if ( initializer instanceof CollectionInitializer ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
final ModelPart initializedPart = initializer.getInitializedPart();
|
final ModelPart initializedPart = initializer.getInitializedPart();
|
||||||
|
|
||||||
if ( initializedPart instanceof EntityInitializer ) {
|
if ( initializedPart instanceof EntityInitializer ) {
|
||||||
|
@ -211,7 +221,7 @@ public class BiDirectionalFetchImpl implements BiDirectionalFetch, Association {
|
||||||
|
|
||||||
NavigablePath path = circularPath.getParent();
|
NavigablePath path = circularPath.getParent();
|
||||||
Initializer parentInitializer = rowProcessingState.resolveInitializer( path );
|
Initializer parentInitializer = rowProcessingState.resolveInitializer( path );
|
||||||
while ( !( parentInitializer instanceof EntityInitializer) && path.getParent() != null ) {
|
while ( !( parentInitializer instanceof EntityInitializer ) && path.getParent() != null ) {
|
||||||
path = path.getParent();
|
path = path.getParent();
|
||||||
parentInitializer = rowProcessingState.resolveInitializer( path );
|
parentInitializer = rowProcessingState.resolveInitializer( path );
|
||||||
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.sql.results.internal.domain;
|
||||||
|
|
||||||
|
import org.hibernate.LockMode;
|
||||||
|
import org.hibernate.engine.FetchTiming;
|
||||||
|
import org.hibernate.metamodel.mapping.Association;
|
||||||
|
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||||
|
import org.hibernate.metamodel.mapping.MappingType;
|
||||||
|
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||||
|
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.query.NavigablePath;
|
||||||
|
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||||
|
import org.hibernate.sql.results.graph.BiDirectionalFetch;
|
||||||
|
import org.hibernate.sql.results.graph.DomainResult;
|
||||||
|
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||||
|
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||||
|
import org.hibernate.sql.results.graph.Fetch;
|
||||||
|
import org.hibernate.sql.results.graph.FetchOptions;
|
||||||
|
import org.hibernate.sql.results.graph.FetchParent;
|
||||||
|
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||||
|
import org.hibernate.sql.results.graph.Fetchable;
|
||||||
|
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||||
|
import org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchInitializer;
|
||||||
|
import org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchInitializer;
|
||||||
|
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||||
|
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||||
|
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Andrea Boriero
|
||||||
|
*/
|
||||||
|
public class CircularFetchImpl implements BiDirectionalFetch, Association {
|
||||||
|
private DomainResult keyResult;
|
||||||
|
private final EntityMappingType entityMappingType;
|
||||||
|
private final FetchTiming timing;
|
||||||
|
private final NavigablePath navigablePath;
|
||||||
|
private final ToOneAttributeMapping fetchable;
|
||||||
|
|
||||||
|
private final FetchParent fetchParent;
|
||||||
|
private final NavigablePath referencedNavigablePath;
|
||||||
|
|
||||||
|
public CircularFetchImpl(
|
||||||
|
EntityMappingType entityMappingType,
|
||||||
|
FetchTiming timing,
|
||||||
|
NavigablePath navigablePath,
|
||||||
|
FetchParent fetchParent,
|
||||||
|
ToOneAttributeMapping fetchable,
|
||||||
|
NavigablePath referencedNavigablePath,
|
||||||
|
DomainResult keyResult) {
|
||||||
|
this.entityMappingType = entityMappingType;
|
||||||
|
this.timing = timing;
|
||||||
|
this.fetchParent = fetchParent;
|
||||||
|
this.navigablePath = navigablePath;
|
||||||
|
this.referencedNavigablePath = referencedNavigablePath;
|
||||||
|
this.fetchable = fetchable;
|
||||||
|
this.keyResult = keyResult;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NavigablePath getNavigablePath() {
|
||||||
|
return navigablePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NavigablePath getReferencedPath() {
|
||||||
|
return referencedNavigablePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FetchParent getFetchParent() {
|
||||||
|
return fetchParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Fetchable getFetchedMapping() {
|
||||||
|
return fetchable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaTypeDescriptor getResultJavaTypeDescriptor() {
|
||||||
|
return fetchable.getJavaTypeDescriptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DomainResultAssembler createAssembler(
|
||||||
|
FetchParentAccess parentAccess,
|
||||||
|
AssemblerCreationState creationState) {
|
||||||
|
final DomainResultAssembler resultAssembler = keyResult.createResultAssembler( creationState );
|
||||||
|
|
||||||
|
final EntityInitializer initializer = (EntityInitializer) creationState.resolveInitializer(
|
||||||
|
getNavigablePath(),
|
||||||
|
() -> {
|
||||||
|
if ( timing == FetchTiming.IMMEDIATE ) {
|
||||||
|
return new EntitySelectFetchInitializer(
|
||||||
|
getReferencedPath(),
|
||||||
|
entityMappingType.getEntityPersister(),
|
||||||
|
resultAssembler,
|
||||||
|
fetchable.isNullable()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new EntityDelayedFetchInitializer(
|
||||||
|
getReferencedPath(),
|
||||||
|
(EntityPersister) ( (AttributeMapping) fetchable ).getMappedTypeDescriptor(),
|
||||||
|
resultAssembler
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return new BiDirectionalFetchAssembler(
|
||||||
|
initializer,
|
||||||
|
fetchable.getJavaTypeDescriptor()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FetchTiming getTiming() {
|
||||||
|
return timing;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasTableGroup() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFetchableName() {
|
||||||
|
return fetchable.getFetchableName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FetchOptions getMappedFetchOptions() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPartName() {
|
||||||
|
return fetchable.getFetchableName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NavigableRole getNavigableRole() {
|
||||||
|
return fetchable.getNavigableRole();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityMappingType findContainingEntityMapping() {
|
||||||
|
return fetchable.findContainingEntityMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MappingType getPartMappingType() {
|
||||||
|
return fetchable.getPartMappingType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaTypeDescriptor getJavaTypeDescriptor() {
|
||||||
|
return fetchable.getJavaTypeDescriptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ForeignKeyDescriptor getForeignKeyDescriptor() {
|
||||||
|
return ( (Association) fetchParent ).getForeignKeyDescriptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Fetch generateFetch(
|
||||||
|
FetchParent fetchParent,
|
||||||
|
NavigablePath fetchablePath,
|
||||||
|
FetchTiming fetchTiming,
|
||||||
|
boolean selected,
|
||||||
|
LockMode lockMode,
|
||||||
|
String resultVariable,
|
||||||
|
DomainResultCreationState creationState) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BiDirectionalFetchAssembler implements DomainResultAssembler {
|
||||||
|
private EntityInitializer initializer;
|
||||||
|
private JavaTypeDescriptor assembledJavaTypeDescriptor;
|
||||||
|
|
||||||
|
public BiDirectionalFetchAssembler(
|
||||||
|
EntityInitializer initializer,
|
||||||
|
JavaTypeDescriptor assembledJavaTypeDescriptor) {
|
||||||
|
this.initializer = initializer;
|
||||||
|
this.assembledJavaTypeDescriptor = assembledJavaTypeDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object assemble(RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) {
|
||||||
|
return initializer.getInitializedInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaTypeDescriptor getAssembledJavaTypeDescriptor() {
|
||||||
|
return assembledJavaTypeDescriptor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.event.spi.EventSource;
|
import org.hibernate.event.spi.EventSource;
|
||||||
import org.hibernate.event.spi.PostLoadEvent;
|
import org.hibernate.event.spi.PostLoadEvent;
|
||||||
import org.hibernate.event.spi.PreLoadEvent;
|
import org.hibernate.event.spi.PreLoadEvent;
|
||||||
|
import org.hibernate.sql.results.graph.Initializer;
|
||||||
import org.hibernate.sql.results.graph.collection.internal.ArrayInitializer;
|
import org.hibernate.sql.results.graph.collection.internal.ArrayInitializer;
|
||||||
import org.hibernate.query.spi.QueryOptions;
|
import org.hibernate.query.spi.QueryOptions;
|
||||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||||
|
@ -41,6 +42,7 @@ public class JdbcValuesSourceProcessingStateStandardImpl implements JdbcValuesSo
|
||||||
private final BiConsumer<EntityKey,LoadingEntityEntry> loadingEntityEntryConsumer;
|
private final BiConsumer<EntityKey,LoadingEntityEntry> loadingEntityEntryConsumer;
|
||||||
|
|
||||||
private Map<EntityKey, LoadingEntityEntry> loadingEntityMap;
|
private Map<EntityKey, LoadingEntityEntry> loadingEntityMap;
|
||||||
|
private Map<EntityKey, Initializer> initializerMap;
|
||||||
private Map<CollectionKey, LoadingCollectionEntry> loadingCollectionMap;
|
private Map<CollectionKey, LoadingCollectionEntry> loadingCollectionMap;
|
||||||
private List<CollectionInitializer> arrayInitializers;
|
private List<CollectionInitializer> arrayInitializers;
|
||||||
|
|
||||||
|
@ -105,6 +107,22 @@ public class JdbcValuesSourceProcessingStateStandardImpl implements JdbcValuesSo
|
||||||
loadingEntityMap.put( entityKey, loadingEntry );
|
loadingEntityMap.put( entityKey, loadingEntry );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerInitilaizer(
|
||||||
|
EntityKey entityKey,
|
||||||
|
Initializer initializer) {
|
||||||
|
if ( initializerMap == null ) {
|
||||||
|
initializerMap = new HashMap<>();
|
||||||
|
}
|
||||||
|
initializerMap.put( entityKey, initializer );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Initializer findInitializer(EntityKey entityKey) {
|
||||||
|
return initializerMap == null ? null : initializerMap.get( entityKey );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoadingEntityEntry findLoadingEntityLocally(EntityKey entityKey) {
|
public LoadingEntityEntry findLoadingEntityLocally(EntityKey entityKey) {
|
||||||
return loadingEntityMap == null ? null : loadingEntityMap.get( entityKey );
|
return loadingEntityMap == null ? null : loadingEntityMap.get( entityKey );
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.hibernate.engine.spi.EntityKey;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.event.spi.PostLoadEvent;
|
import org.hibernate.event.spi.PostLoadEvent;
|
||||||
import org.hibernate.event.spi.PreLoadEvent;
|
import org.hibernate.event.spi.PreLoadEvent;
|
||||||
|
import org.hibernate.sql.results.graph.Initializer;
|
||||||
import org.hibernate.sql.results.spi.LoadContexts;
|
import org.hibernate.sql.results.spi.LoadContexts;
|
||||||
import org.hibernate.sql.results.graph.collection.LoadingCollectionEntry;
|
import org.hibernate.sql.results.graph.collection.LoadingCollectionEntry;
|
||||||
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
|
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
|
||||||
|
@ -55,6 +56,13 @@ public interface JdbcValuesSourceProcessingState {
|
||||||
EntityKey entityKey,
|
EntityKey entityKey,
|
||||||
LoadingEntityEntry loadingEntry);
|
LoadingEntityEntry loadingEntry);
|
||||||
|
|
||||||
|
void registerInitilaizer(
|
||||||
|
EntityKey entityKey,
|
||||||
|
Initializer initializer);
|
||||||
|
|
||||||
|
Initializer findInitializer(EntityKey entityKey);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a LoadingCollectionEntry locally to this context.
|
* Find a LoadingCollectionEntry locally to this context.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue