Throw MultipleBagFetchException when trying to fetch multiple bags
This commit is contained in:
parent
fc914ea647
commit
842c4f18c9
|
@ -12,15 +12,14 @@ import java.util.Iterator;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.collection.spi.BagSemantics;
|
||||
import org.hibernate.collection.spi.CollectionInitializerProducer;
|
||||
import org.hibernate.collection.spi.CollectionSemantics;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.collection.internal.BagInitializerProducer;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
|
@ -29,7 +28,7 @@ import org.hibernate.sql.results.graph.FetchParent;
|
|||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractBagSemantics<B extends Collection<?>> implements CollectionSemantics<B> {
|
||||
public abstract class AbstractBagSemantics<B extends Collection<?>> implements BagSemantics<B> {
|
||||
@Override
|
||||
public Class<B> getCollectionJavaType() {
|
||||
//noinspection unchecked
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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.collection.spi;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public interface BagSemantics<B> extends CollectionSemantics<B> {
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.collection.spi.BagSemantics;
|
||||
import org.hibernate.engine.FetchStyle;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.profile.FetchProfile;
|
||||
|
@ -27,6 +28,7 @@ import org.hibernate.graph.GraphSemantic;
|
|||
import org.hibernate.graph.spi.RootGraphImplementor;
|
||||
import org.hibernate.internal.FilterHelper;
|
||||
import org.hibernate.internal.util.MutableInteger;
|
||||
import org.hibernate.loader.MultipleBagFetchException;
|
||||
import org.hibernate.loader.ast.spi.Loadable;
|
||||
import org.hibernate.loader.ast.spi.Loader;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||
|
@ -43,6 +45,7 @@ import org.hibernate.metamodel.mapping.ordering.OrderByFragment;
|
|||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.Joinable;
|
||||
import org.hibernate.query.ComparisonOperator;
|
||||
import org.hibernate.query.EntityIdentifierNavigablePath;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.spi.SimpleFromClauseAccessImpl;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseManager;
|
||||
|
@ -443,7 +446,8 @@ public class LoaderSelectBuilder {
|
|||
}
|
||||
|
||||
final List<Fetch> fetches = new ArrayList<>();
|
||||
final BiConsumer<Fetchable, Boolean> processor = createFetchableBiConsumer( fetchParent, querySpec, creationState, fetches );
|
||||
final List<String> bagRoles = new ArrayList<>();
|
||||
final BiConsumer<Fetchable, Boolean> processor = createFetchableBiConsumer( fetchParent, querySpec, creationState, fetches, bagRoles );
|
||||
|
||||
final FetchableContainer referencedMappingContainer = fetchParent.getReferencedMappingContainer();
|
||||
if ( fetchParent.getNavigablePath().getParent() != null ) {
|
||||
|
@ -452,6 +456,9 @@ public class LoaderSelectBuilder {
|
|||
}
|
||||
referencedMappingContainer.visitFetchables(
|
||||
fetchable -> processor.accept( fetchable, false ), null );
|
||||
if ( bagRoles.size() > 1 ) {
|
||||
throw new MultipleBagFetchException( bagRoles );
|
||||
}
|
||||
return fetches;
|
||||
}
|
||||
|
||||
|
@ -459,11 +466,12 @@ public class LoaderSelectBuilder {
|
|||
FetchParent fetchParent,
|
||||
QuerySpec querySpec,
|
||||
LoaderSqlAstCreationState creationState,
|
||||
List<Fetch> fetches) {
|
||||
List<Fetch> fetches,
|
||||
List<String> bagRoles) {
|
||||
return (fetchable, isKeyFetchable) -> {
|
||||
NavigablePath panrentNavigablePath = fetchParent.getNavigablePath();
|
||||
if ( isKeyFetchable ) {
|
||||
panrentNavigablePath = panrentNavigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME );
|
||||
panrentNavigablePath = new EntityIdentifierNavigablePath( panrentNavigablePath );
|
||||
}
|
||||
final NavigablePath fetchablePath = panrentNavigablePath.append( fetchable.getFetchableName() );
|
||||
|
||||
|
@ -542,23 +550,31 @@ public class LoaderSelectBuilder {
|
|||
null,
|
||||
creationState
|
||||
);
|
||||
fetches.add( fetch );
|
||||
|
||||
if ( fetchable instanceof PluralAttributeMapping && fetchTiming == FetchTiming.IMMEDIATE && joined ) {
|
||||
final TableGroup joinTableGroup = creationState.getFromClauseAccess().getTableGroup( fetchablePath );
|
||||
if ( fetch.getTiming() == FetchTiming.IMMEDIATE && fetchable instanceof PluralAttributeMapping ) {
|
||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) fetchable;
|
||||
applyFiltering(
|
||||
querySpec,
|
||||
joinTableGroup,
|
||||
pluralAttributeMapping
|
||||
);
|
||||
applyOrdering(
|
||||
querySpec,
|
||||
fetchablePath,
|
||||
pluralAttributeMapping,
|
||||
creationState
|
||||
);
|
||||
if ( pluralAttributeMapping.getMappedTypeDescriptor()
|
||||
.getCollectionSemantics() instanceof BagSemantics ) {
|
||||
bagRoles.add( fetchable.getNavigableRole().getNavigableName() );
|
||||
}
|
||||
if ( joined ) {
|
||||
final TableGroup joinTableGroup = creationState.getFromClauseAccess()
|
||||
.getTableGroup( fetchablePath );
|
||||
applyFiltering(
|
||||
querySpec,
|
||||
joinTableGroup,
|
||||
pluralAttributeMapping
|
||||
);
|
||||
applyOrdering(
|
||||
querySpec,
|
||||
fetchablePath,
|
||||
pluralAttributeMapping,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fetches.add( fetch );
|
||||
}
|
||||
finally {
|
||||
if ( changeFetchDepth ) {
|
||||
|
|
|
@ -48,7 +48,16 @@ public class SingleIdEntityLoaderStandardImpl<T> extends SingleIdEntityLoaderSup
|
|||
public void prepare() {
|
||||
// see `org.hibernate.persister.entity.AbstractEntityPersister#createLoaders`
|
||||
// we should pre-load a few - maybe LockMode.NONE and LockMode.READ
|
||||
|
||||
final LockOptions lockOptions = LockOptions.NONE;
|
||||
final LoadQueryInfluencers queryInfluencers = new LoadQueryInfluencers( sessionFactory );
|
||||
final SingleIdLoadPlan<T> plan = createLoadPlan(
|
||||
lockOptions,
|
||||
queryInfluencers,
|
||||
sessionFactory
|
||||
);
|
||||
if ( determineIfReusable( lockOptions, queryInfluencers ) ) {
|
||||
selectByLockMode.put( lockOptions.getLockMode(), plan );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,7 +16,7 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
|
|||
*/
|
||||
public abstract class SingleIdEntityLoaderSupport<T> implements SingleIdEntityLoader<T> {
|
||||
private final EntityMappingType entityDescriptor;
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
protected final SessionFactoryImplementor sessionFactory;
|
||||
|
||||
private DatabaseSnapshotExecutor databaseSnapshotExecutor;
|
||||
|
||||
|
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* 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.internal;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.engine.FetchStyle;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.ColumnConsumer;
|
||||
import org.hibernate.metamodel.mapping.CompositeIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.tree.from.CompositeTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
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.embeddable.EmbeddableValuedFetchable;
|
||||
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl;
|
||||
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public abstract class AbstractCompositeIdentifierMapping
|
||||
implements CompositeIdentifierMapping, EmbeddableValuedFetchable, FetchOptions {
|
||||
private final NavigableRole navigableRole;
|
||||
private final String tableExpression;
|
||||
|
||||
private final StateArrayContributorMetadataAccess attributeMetadataAccess;
|
||||
private final List<String> columnNames;
|
||||
|
||||
private final EntityMappingType entityMapping;
|
||||
private final EmbeddableMappingType embeddableDescriptor;
|
||||
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
|
||||
public AbstractCompositeIdentifierMapping(
|
||||
StateArrayContributorMetadataAccess attributeMetadataAccess,
|
||||
EmbeddableMappingType embeddableDescriptor,
|
||||
EntityMappingType entityMapping,
|
||||
String tableExpression,
|
||||
String[] columnNames,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
this.attributeMetadataAccess = attributeMetadataAccess;
|
||||
this.embeddableDescriptor = embeddableDescriptor;
|
||||
this.entityMapping = entityMapping;
|
||||
this.tableExpression = tableExpression;
|
||||
this.sessionFactory = sessionFactory;
|
||||
|
||||
this.columnNames = Arrays.asList( columnNames );
|
||||
|
||||
this.navigableRole = entityMapping.getNavigableRole()
|
||||
.appendContainer( EntityIdentifierMapping.ROLE_LOCAL_NAME );
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableMappingType getMappedTypeDescriptor() {
|
||||
return embeddableDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableMappingType getPartMappingType() {
|
||||
return getEmbeddableTypeDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaTypeDescriptor getJavaTypeDescriptor() {
|
||||
return getEmbeddableTypeDescriptor().getMappedJavaTypeDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableMappingType getEmbeddableTypeDescriptor() {
|
||||
return embeddableDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContainingTableExpression() {
|
||||
return tableExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableRole getNavigableRole() {
|
||||
return navigableRole;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMappedColumnExpressions() {
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitColumns(ColumnConsumer consumer) {
|
||||
getAttributes().forEach(
|
||||
attribute -> {
|
||||
if ( attribute instanceof ToOneAttributeMapping ) {
|
||||
final ToOneAttributeMapping associationAttributeMapping = (ToOneAttributeMapping) attribute;
|
||||
associationAttributeMapping.getForeignKeyDescriptor().visitReferringColumns( consumer );
|
||||
}
|
||||
else {
|
||||
attribute.visitColumns( consumer );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetch generateFetch(
|
||||
FetchParent fetchParent,
|
||||
NavigablePath fetchablePath,
|
||||
FetchTiming fetchTiming,
|
||||
boolean selected,
|
||||
LockMode lockMode,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
return new EmbeddableFetchImpl(
|
||||
fetchablePath,
|
||||
this,
|
||||
fetchParent,
|
||||
fetchTiming,
|
||||
selected,
|
||||
attributeMetadataAccess.resolveAttributeMetadata( null ).isNullable(),
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableGroupJoin createTableGroupJoin(
|
||||
NavigablePath navigablePath,
|
||||
TableGroup lhs,
|
||||
String explicitSourceAlias,
|
||||
SqlAstJoinType sqlAstJoinType,
|
||||
LockMode lockMode,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final CompositeTableGroup compositeTableGroup = new CompositeTableGroup(
|
||||
navigablePath,
|
||||
this,
|
||||
lhs
|
||||
);
|
||||
|
||||
final TableGroupJoin join = new TableGroupJoin( navigablePath, SqlAstJoinType.LEFT, compositeTableGroup, null );
|
||||
lhs.addTableGroupJoin( join );
|
||||
|
||||
return join;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
|
||||
return embeddableDescriptor.findSubPart( name, treatTargetType );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSubParts(
|
||||
Consumer<ModelPart> consumer,
|
||||
EntityMappingType treatTargetType) {
|
||||
embeddableDescriptor.visitSubParts( consumer, treatTargetType );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJdbcTypes(
|
||||
Consumer<JdbcMapping> action,
|
||||
Clause clause,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
embeddableDescriptor.visitJdbcTypes( action, clause, typeConfiguration );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DomainResult<T> createDomainResult(
|
||||
NavigablePath navigablePath,
|
||||
TableGroup tableGroup,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
return new EmbeddableResultImpl<>(
|
||||
navigablePath,
|
||||
this,
|
||||
resultVariable,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiate() {
|
||||
return getEntityMapping().getRepresentationStrategy().getInstantiator().instantiate( sessionFactory );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingularAttributeMapping getParentInjectionAttributeMapping() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType findContainingEntityMapping() {
|
||||
return entityMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchOptions getMappedFetchOptions() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchStyle getStyle() {
|
||||
return FetchStyle.JOIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchTiming getTiming() {
|
||||
return FetchTiming.IMMEDIATE;
|
||||
}
|
||||
|
||||
protected EntityMappingType getEntityMapping() {
|
||||
return entityMapping;
|
||||
}
|
||||
}
|
|
@ -28,6 +28,9 @@ public interface PluralAttributeMapping
|
|||
|
||||
CollectionPart getIndexDescriptor();
|
||||
|
||||
@Override
|
||||
CollectionMappingType getMappedTypeDescriptor();
|
||||
|
||||
interface IndexMetadata {
|
||||
CollectionPart getIndexDescriptor();
|
||||
int getListIndexBase();
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.hibernate.metamodel.mapping.MappingType;
|
|||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.query.EntityIdentifierNavigablePath;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
|
@ -187,9 +188,14 @@ public class BasicValuedCollectionPart
|
|||
nature.getName()
|
||||
);
|
||||
|
||||
NavigablePath parentNavigablePath = fetchablePath.getParent();
|
||||
if ( parentNavigablePath instanceof EntityIdentifierNavigablePath ) {
|
||||
parentNavigablePath = parentNavigablePath.getParent();
|
||||
}
|
||||
|
||||
final TableGroup tableGroup = creationState.getSqlAstCreationState()
|
||||
.getFromClauseAccess()
|
||||
.findTableGroup( fetchablePath.getParent() );
|
||||
.findTableGroup( parentNavigablePath );
|
||||
final SqlSelection sqlSelection = resolveSqlSelection( tableGroup, creationState );
|
||||
|
||||
return new BasicFetch(
|
||||
|
|
|
@ -6,102 +6,62 @@
|
|||
*/
|
||||
package org.hibernate.metamodel.mapping.internal;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.engine.FetchStyle;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.metamodel.mapping.ColumnConsumer;
|
||||
import org.hibernate.metamodel.mapping.CompositeIdentifierMapping;
|
||||
import org.hibernate.metamodel.internal.AbstractCompositeIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.MappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.property.access.spi.PropertyAccess;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||
import org.hibernate.sql.ast.tree.from.CompositeTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
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.embeddable.EmbeddableValuedFetchable;
|
||||
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl;
|
||||
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* Support for {@link javax.persistence.EmbeddedId}
|
||||
*
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class EmbeddedIdentifierMappingImpl
|
||||
implements CompositeIdentifierMapping, EmbeddableValuedFetchable, FetchOptions {
|
||||
private final NavigableRole navigableRole;
|
||||
private final EntityMappingType entityMapping;
|
||||
public class EmbeddedIdentifierMappingImpl extends AbstractCompositeIdentifierMapping {
|
||||
private final String name;
|
||||
private final MappingType type;
|
||||
private final StateArrayContributorMetadataAccess attributeMetadataAccess;
|
||||
private final PropertyAccess propertyAccess;
|
||||
private final String tableExpression;
|
||||
private final String[] attrColumnNames;
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public EmbeddedIdentifierMappingImpl(
|
||||
EntityMappingType entityMapping,
|
||||
String name,
|
||||
MappingType type,
|
||||
EmbeddableMappingType embeddableDescriptor,
|
||||
StateArrayContributorMetadataAccess attributeMetadataAccess,
|
||||
PropertyAccess propertyAccess,
|
||||
String tableExpression,
|
||||
String[] attrColumnNames,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
this.navigableRole = entityMapping.getNavigableRole().appendContainer( EntityIdentifierMapping.ROLE_LOCAL_NAME );
|
||||
this.entityMapping = entityMapping;
|
||||
super(
|
||||
attributeMetadataAccess,
|
||||
embeddableDescriptor,
|
||||
entityMapping,
|
||||
tableExpression,
|
||||
attrColumnNames,
|
||||
sessionFactory
|
||||
);
|
||||
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.attributeMetadataAccess = attributeMetadataAccess;
|
||||
this.propertyAccess = propertyAccess;
|
||||
this.tableExpression = tableExpression;
|
||||
this.attrColumnNames = attrColumnNames;
|
||||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableMappingType getPartMappingType() {
|
||||
return (EmbeddableMappingType) type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaTypeDescriptor getJavaTypeDescriptor() {
|
||||
return getMappedTypeDescriptor().getMappedJavaTypeDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -109,11 +69,6 @@ public class EmbeddedIdentifierMappingImpl
|
|||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableRole getNavigableRole() {
|
||||
return navigableRole;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getIdentifier(Object entity, SharedSessionContractImplementor session) {
|
||||
if ( entity instanceof HibernateProxy ) {
|
||||
|
@ -127,44 +82,6 @@ public class EmbeddedIdentifierMappingImpl
|
|||
propertyAccess.getSetter().set( entity, id, session.getFactory() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiate() {
|
||||
return entityMapping.getRepresentationStrategy().getInstantiator().instantiate( sessionFactory );
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableMappingType getEmbeddableTypeDescriptor() {
|
||||
return getMappedTypeDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableMappingType getMappedTypeDescriptor() {
|
||||
return (EmbeddableMappingType) type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContainingTableExpression() {
|
||||
return tableExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMappedColumnExpressions() {
|
||||
return Arrays.asList( attrColumnNames );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingularAttributeMapping getParentInjectionAttributeMapping() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJdbcTypes(
|
||||
Consumer<JdbcMapping> action,
|
||||
Clause clause,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
getMappedTypeDescriptor().visitJdbcTypes( action,clause,typeConfiguration );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJdbcValues(
|
||||
Object value,
|
||||
|
@ -189,27 +106,14 @@ public class EmbeddedIdentifierMappingImpl
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DomainResult<T> createDomainResult(
|
||||
NavigablePath navigablePath,
|
||||
TableGroup tableGroup,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
return new EmbeddableResultImpl<>(
|
||||
navigablePath,
|
||||
this,
|
||||
resultVariable,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression toSqlExpression(
|
||||
TableGroup tableGroup,
|
||||
Clause clause,
|
||||
SqmToSqlAstConverter walker,
|
||||
SqlAstCreationState sqlAstCreationState) {
|
||||
final List<ColumnReference> columnReferences = CollectionHelper.arrayList( attrColumnNames.length );
|
||||
final List<String> attrColumnNames = getMappedColumnExpressions();
|
||||
final List<ColumnReference> columnReferences = CollectionHelper.arrayList( attrColumnNames.size() );
|
||||
final TableReference tableReference = tableGroup.resolveTableReference( getContainingTableExpression() );
|
||||
getEmbeddableTypeDescriptor().visitJdbcTypes(
|
||||
new Consumer<JdbcMapping>() {
|
||||
|
@ -217,21 +121,22 @@ public class EmbeddedIdentifierMappingImpl
|
|||
|
||||
@Override
|
||||
public void accept(JdbcMapping jdbcMapping) {
|
||||
final String attrColumnExpr = attrColumnNames[ index++ ];
|
||||
final String attrColumnExpr = attrColumnNames.get( index++ );
|
||||
|
||||
final Expression columnReference = sqlAstCreationState.getSqlExpressionResolver().resolveSqlExpression(
|
||||
SqlExpressionResolver.createColumnReferenceKey(
|
||||
tableReference,
|
||||
attrColumnExpr
|
||||
),
|
||||
sqlAstProcessingState -> new ColumnReference(
|
||||
tableReference.getIdentificationVariable(),
|
||||
attrColumnExpr,
|
||||
false,
|
||||
jdbcMapping,
|
||||
sqlAstCreationState.getCreationContext().getSessionFactory()
|
||||
)
|
||||
);
|
||||
final Expression columnReference = sqlAstCreationState.getSqlExpressionResolver()
|
||||
.resolveSqlExpression(
|
||||
SqlExpressionResolver.createColumnReferenceKey(
|
||||
tableReference,
|
||||
attrColumnExpr
|
||||
),
|
||||
sqlAstProcessingState -> new ColumnReference(
|
||||
tableReference.getIdentificationVariable(),
|
||||
attrColumnExpr,
|
||||
false,
|
||||
jdbcMapping,
|
||||
sqlAstCreationState.getCreationContext().getSessionFactory()
|
||||
)
|
||||
);
|
||||
|
||||
columnReferences.add( (ColumnReference) columnReference );
|
||||
}
|
||||
|
@ -243,99 +148,23 @@ public class EmbeddedIdentifierMappingImpl
|
|||
return new SqlTuple( columnReferences, this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableGroupJoin createTableGroupJoin(
|
||||
NavigablePath navigablePath,
|
||||
TableGroup lhs,
|
||||
String explicitSourceAlias,
|
||||
SqlAstJoinType sqlAstJoinType,
|
||||
LockMode lockMode,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final CompositeTableGroup compositeTableGroup = new CompositeTableGroup(
|
||||
navigablePath,
|
||||
this,
|
||||
lhs
|
||||
);
|
||||
|
||||
final TableGroupJoin join = new TableGroupJoin( navigablePath, SqlAstJoinType.LEFT, compositeTableGroup, null );
|
||||
lhs.addTableGroupJoin( join );
|
||||
|
||||
return join;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSqlAliasStem() {
|
||||
return name;
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
|
||||
return getMappedTypeDescriptor().findSubPart( name, treatTargetType );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSubParts(
|
||||
Consumer<ModelPart> consumer,
|
||||
EntityMappingType treatTargetType) {
|
||||
getMappedTypeDescriptor().visitSubParts( consumer, treatTargetType );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFetchableName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchOptions getMappedFetchOptions() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetch generateFetch(
|
||||
FetchParent fetchParent,
|
||||
NavigablePath fetchablePath,
|
||||
FetchTiming fetchTiming,
|
||||
boolean selected,
|
||||
LockMode lockMode,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
return new EmbeddableFetchImpl(
|
||||
fetchablePath,
|
||||
this,
|
||||
fetchParent,
|
||||
fetchTiming,
|
||||
selected,
|
||||
attributeMetadataAccess.resolveAttributeMetadata( null ).isNullable(),
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumberOfFetchables() {
|
||||
return getEmbeddableTypeDescriptor().getNumberOfAttributeMappings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitColumns(ColumnConsumer consumer) {
|
||||
getAttributes().forEach(
|
||||
attribute -> {
|
||||
if ( attribute instanceof ToOneAttributeMapping ) {
|
||||
final ToOneAttributeMapping associationAttributeMapping = (ToOneAttributeMapping) attribute;
|
||||
associationAttributeMapping.getForeignKeyDescriptor().visitReferringColumns( consumer );
|
||||
}
|
||||
else {
|
||||
attribute.visitColumns( consumer );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType findContainingEntityMapping() {
|
||||
return entityMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAttributeCount() {
|
||||
|
@ -348,13 +177,4 @@ public class EmbeddedIdentifierMappingImpl
|
|||
return (Collection) getEmbeddableTypeDescriptor().getAttributeMappings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchStyle getStyle() {
|
||||
return FetchStyle.JOIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchTiming getTiming() {
|
||||
return FetchTiming.IMMEDIATE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,67 +6,34 @@
|
|||
*/
|
||||
package org.hibernate.metamodel.mapping.internal;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.engine.FetchStyle;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.metamodel.mapping.CompositeIdentifierMapping;
|
||||
import org.hibernate.metamodel.internal.AbstractCompositeIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.from.CompositeTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
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.embeddable.EmbeddableValuedFetchable;
|
||||
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl;
|
||||
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
|
||||
/**
|
||||
* A "non-aggregated" composite identifier.
|
||||
*
|
||||
* <p>
|
||||
* This is an identifier mapped using JPA's {@link javax.persistence.MapsId} feature.
|
||||
*
|
||||
* @apiNote Technically a MapsId id does not have to be composite; we still handle that this class however
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
* @apiNote Technically a MapsId id does not have to be composite; we still handle that this class however
|
||||
*/
|
||||
public class NonAggregatedIdentifierMappingImpl
|
||||
implements CompositeIdentifierMapping, EmbeddableValuedFetchable, FetchOptions {
|
||||
private final EmbeddableMappingType embeddableDescriptor;
|
||||
private final NavigableRole navigableRole;
|
||||
private final EntityMappingType entityMapping;
|
||||
public class NonAggregatedIdentifierMappingImpl extends AbstractCompositeIdentifierMapping {
|
||||
|
||||
private final List<SingularAttributeMapping> idAttributeMappings;
|
||||
|
||||
private final StateArrayContributorMetadataAccess attributeMetadataAccess;
|
||||
private final String rootTableName;
|
||||
private final List<String> idColumnNames;
|
||||
|
||||
public NonAggregatedIdentifierMappingImpl(
|
||||
EmbeddableMappingType embeddableDescriptor,
|
||||
EntityMappingType entityMapping,
|
||||
|
@ -78,35 +45,15 @@ public class NonAggregatedIdentifierMappingImpl
|
|||
Component bootIdClassDescriptor,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
// todo (6.0) : handle MapsId and IdClass
|
||||
|
||||
this.embeddableDescriptor = embeddableDescriptor;
|
||||
this.entityMapping = entityMapping;
|
||||
super(
|
||||
attributeMetadataAccess,
|
||||
embeddableDescriptor,
|
||||
entityMapping,
|
||||
rootTableName,
|
||||
rootTableKeyColumnNames,
|
||||
creationProcess.getCreationContext().getSessionFactory()
|
||||
);
|
||||
this.idAttributeMappings = idAttributeMappings;
|
||||
this.attributeMetadataAccess = attributeMetadataAccess;
|
||||
this.rootTableName = rootTableName;
|
||||
this.idColumnNames = Arrays.asList( rootTableKeyColumnNames );
|
||||
|
||||
this.navigableRole = entityMapping.getNavigableRole().appendContainer( EntityIdentifierMapping.ROLE_LOCAL_NAME );
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType getPartMappingType() {
|
||||
return entityMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaTypeDescriptor getJavaTypeDescriptor() {
|
||||
return entityMapping.getJavaTypeDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableRole getNavigableRole() {
|
||||
return navigableRole;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableMappingType getMappedTypeDescriptor() {
|
||||
return embeddableDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -128,56 +75,9 @@ public class NonAggregatedIdentifierMappingImpl
|
|||
public void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiate() {
|
||||
return entityMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DomainResult<T> createDomainResult(
|
||||
NavigablePath navigablePath,
|
||||
TableGroup tableGroup,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
return new EmbeddableResultImpl<>(
|
||||
navigablePath,
|
||||
this,
|
||||
resultVariable,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType findContainingEntityMapping() {
|
||||
return entityMapping;
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// EmbeddableValuedFetchable
|
||||
|
||||
|
||||
@Override
|
||||
public EmbeddableMappingType getEmbeddableTypeDescriptor() {
|
||||
return getMappedTypeDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContainingTableExpression() {
|
||||
return rootTableName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMappedColumnExpressions() {
|
||||
return idColumnNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingularAttributeMapping getParentInjectionAttributeMapping() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression toSqlExpression(
|
||||
TableGroup tableGroup,
|
||||
|
@ -187,85 +87,19 @@ public class NonAggregatedIdentifierMappingImpl
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableGroupJoin createTableGroupJoin(
|
||||
NavigablePath navigablePath,
|
||||
TableGroup lhs,
|
||||
String explicitSourceAlias,
|
||||
SqlAstJoinType sqlAstJoinType,
|
||||
LockMode lockMode,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final CompositeTableGroup compositeTableGroup = new CompositeTableGroup(
|
||||
navigablePath,
|
||||
this,
|
||||
lhs
|
||||
);
|
||||
|
||||
final TableGroupJoin join = new TableGroupJoin( navigablePath, SqlAstJoinType.LEFT, compositeTableGroup, null );
|
||||
lhs.addTableGroupJoin( join );
|
||||
|
||||
return join;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSqlAliasStem() {
|
||||
return "id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
|
||||
return getMappedTypeDescriptor().findSubPart( name, treatTargetType );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSubParts(Consumer<ModelPart> consumer, EntityMappingType treatTargetType) {
|
||||
getMappedTypeDescriptor().visitSubParts( consumer, treatTargetType );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFetchableName() {
|
||||
return "id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchOptions getMappedFetchOptions() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetch generateFetch(
|
||||
FetchParent fetchParent,
|
||||
NavigablePath fetchablePath,
|
||||
FetchTiming fetchTiming,
|
||||
boolean selected,
|
||||
LockMode lockMode,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
return new EmbeddableFetchImpl(
|
||||
fetchablePath,
|
||||
this,
|
||||
fetchParent,
|
||||
fetchTiming,
|
||||
selected,
|
||||
attributeMetadataAccess.resolveAttributeMetadata( null ).isNullable(),
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumberOfFetchables() {
|
||||
return idAttributeMappings.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchStyle getStyle() {
|
||||
return FetchStyle.JOIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchTiming getTiming() {
|
||||
return FetchTiming.IMMEDIATE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
|||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.property.access.spi.PropertyAccess;
|
||||
import org.hibernate.query.EntityIdentifierNavigablePath;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
|
@ -50,8 +51,8 @@ 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.EntityFetchSelectImpl;
|
||||
import org.hibernate.sql.results.graph.entity.internal.EntityResultJoinedSubclassImpl;
|
||||
import org.hibernate.sql.results.internal.domain.CircularFetchImpl;
|
||||
import org.hibernate.sql.results.internal.domain.CircularBiDirectionalFetchImpl;
|
||||
import org.hibernate.sql.results.internal.domain.CircularFetchImpl;
|
||||
import org.hibernate.type.ForeignKeyDirection;
|
||||
|
||||
/**
|
||||
|
@ -227,7 +228,7 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
|||
NavigablePath parentNavigablePath = fetchablePath.getParent();
|
||||
ModelPart modelPart = creationState.resolveModelPart( parentNavigablePath );
|
||||
if ( modelPart instanceof EmbeddedIdentifierMappingImpl ) {
|
||||
while ( parentNavigablePath.getFullPath().endsWith( EntityIdentifierMapping.ROLE_LOCAL_NAME ) ) {
|
||||
while ( parentNavigablePath instanceof EntityIdentifierNavigablePath ) {
|
||||
parentNavigablePath = parentNavigablePath.getParent();
|
||||
}
|
||||
}
|
||||
|
@ -340,7 +341,7 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
|||
}
|
||||
|
||||
if ( modelPart instanceof EntityCollectionPart ) {
|
||||
if ( parentOfParent.getFullPath().endsWith( EntityIdentifierMapping.ROLE_LOCAL_NAME ) ) {
|
||||
if ( parentOfParent instanceof EntityIdentifierNavigablePath ) {
|
||||
parentOfParent = parentOfParent.getParent();
|
||||
}
|
||||
return ( (PluralAttributeMapping) creationState.resolveModelPart( parentOfParent ) ).isBidirectionalAttributeName(
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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.query;
|
||||
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class EntityIdentifierNavigablePath extends NavigablePath {
|
||||
|
||||
public EntityIdentifierNavigablePath(NavigablePath parent) {
|
||||
super( parent, EntityIdentifierMapping.ROLE_LOCAL_NAME );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocalName() {
|
||||
return EntityIdentifierMapping.ROLE_LOCAL_NAME;
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ import org.hibernate.HibernateException;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.collection.spi.BagSemantics;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.profile.FetchProfile;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
|
@ -26,6 +27,7 @@ import org.hibernate.internal.FilterHelper;
|
|||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.internal.util.collections.Stack;
|
||||
import org.hibernate.internal.util.collections.StandardStack;
|
||||
import org.hibernate.loader.MultipleBagFetchException;
|
||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
|
@ -271,6 +273,7 @@ public class StandardSqmSelectTranslator
|
|||
@Override
|
||||
public List<Fetch> visitFetches(FetchParent fetchParent) {
|
||||
final List<Fetch> fetches = CollectionHelper.arrayList( fetchParent.getReferencedMappingType().getNumberOfFetchables() );
|
||||
final List<String> bagRoles = new ArrayList<>();
|
||||
|
||||
final BiConsumer<Fetchable, Boolean> fetchableBiConsumer = (fetchable, isKeyFetchable) -> {
|
||||
final NavigablePath fetchablePath = fetchParent.getNavigablePath().append( fetchable.getFetchableName() );
|
||||
|
@ -291,6 +294,12 @@ public class StandardSqmSelectTranslator
|
|||
final Fetch fetch = buildFetch( fetchablePath, fetchParent, fetchable, isKeyFetchable );
|
||||
|
||||
if ( fetch != null ) {
|
||||
if ( fetch.getTiming() == FetchTiming.IMMEDIATE &&
|
||||
fetchable instanceof PluralAttributeMapping &&
|
||||
( (PluralAttributeMapping) fetchable ).getMappedTypeDescriptor()
|
||||
.getCollectionSemantics() instanceof BagSemantics ) {
|
||||
bagRoles.add( fetchable.getNavigableRole().getNavigableName() );
|
||||
}
|
||||
fetches.add( fetch );
|
||||
}
|
||||
}
|
||||
|
@ -304,7 +313,9 @@ public class StandardSqmSelectTranslator
|
|||
// fetchParent.getReferencedMappingContainer().visitFetchables( fetchableBiConsumer, treatTargetType );
|
||||
fetchParent.getReferencedMappingContainer().visitKeyFetchables( fetchable -> fetchableBiConsumer.accept( fetchable, true ), null );
|
||||
fetchParent.getReferencedMappingContainer().visitFetchables( fetchable -> fetchableBiConsumer.accept( fetchable, false ), null );
|
||||
|
||||
if ( bagRoles.size() > 1 ) {
|
||||
throw new MultipleBagFetchException( bagRoles );
|
||||
}
|
||||
return fetches;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.hibernate.metamodel.mapping.ManagedMappingType;
|
|||
import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
import org.hibernate.persister.entity.AbstractEntityPersister;
|
||||
import org.hibernate.query.EntityIdentifierNavigablePath;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.results.graph.AbstractFetchParent;
|
||||
|
@ -71,7 +72,7 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent
|
|||
identifierResult = null;
|
||||
if ( identifierMapping instanceof SingleAttributeIdentifierMapping ) {
|
||||
identifierMapping.createDomainResult(
|
||||
navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ),
|
||||
new EntityIdentifierNavigablePath( navigablePath ),
|
||||
entityTableGroup,
|
||||
null,
|
||||
creationState
|
||||
|
@ -83,7 +84,7 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent
|
|||
}
|
||||
else {
|
||||
identifierResult = identifierMapping.createDomainResult(
|
||||
navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ),
|
||||
new EntityIdentifierNavigablePath( navigablePath ),
|
||||
entityTableGroup,
|
||||
null,
|
||||
creationState
|
||||
|
@ -141,7 +142,7 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent
|
|||
attributeMapping -> {
|
||||
if ( attributeMapping instanceof ToOneAttributeMapping ) {
|
||||
( (ToOneAttributeMapping) attributeMapping ).getForeignKeyDescriptor().createDomainResult(
|
||||
navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ),
|
||||
new EntityIdentifierNavigablePath( navigablePath ),
|
||||
entityTableGroup,
|
||||
null,
|
||||
creationState
|
||||
|
@ -149,7 +150,7 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent
|
|||
}
|
||||
else {
|
||||
attributeMapping.createDomainResult(
|
||||
navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ),
|
||||
new EntityIdentifierNavigablePath( navigablePath ),
|
||||
entityTableGroup,
|
||||
null,
|
||||
creationState
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* 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.orm.test.collection.bag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.loader.MultipleBagFetchException;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
@DomainModel(
|
||||
annotatedClasses = {
|
||||
MultipleBagFetchHqlTest.Post.class,
|
||||
MultipleBagFetchHqlTest.PostComment.class,
|
||||
MultipleBagFetchHqlTest.Tag.class
|
||||
}
|
||||
)
|
||||
@SessionFactory
|
||||
public class MultipleBagFetchHqlTest {
|
||||
|
||||
@BeforeEach
|
||||
public void setUp(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Post post = new Post();
|
||||
post.setId( 1L );
|
||||
post.setTitle( String.format( "Post nr. %d", 1 ) );
|
||||
PostComment comment = new PostComment();
|
||||
comment.setId( 1L );
|
||||
comment.setReview( "Excellent!" );
|
||||
session.persist( post );
|
||||
session.persist( comment );
|
||||
post.comments.add( comment );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete from Post" ).executeUpdate();
|
||||
session.createQuery( "delete from Tag" ).executeUpdate();
|
||||
session.createQuery( "delete from PostComment" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleBagFetchHql(SessionFactoryScope scope) {
|
||||
scope.inSession(
|
||||
session -> {
|
||||
try {
|
||||
session.createQuery(
|
||||
"select p " +
|
||||
"from Post p " +
|
||||
"join fetch p.tags " +
|
||||
"join fetch p.comments " +
|
||||
"where p.id = :id"
|
||||
).setParameter( "id", 1L ).uniqueResult();
|
||||
fail( "Should throw org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags" );
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
session.getTransaction().rollback();
|
||||
// MultipleBagFetchException was converted to IllegalArgumentException
|
||||
assertTrue( MultipleBagFetchException.class.isInstance( expected.getCause() ) );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleBagFetchHql(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
session.createQuery(
|
||||
"select p " +
|
||||
"from Post p " +
|
||||
"join fetch p.tags " +
|
||||
"join p.comments " +
|
||||
"where p.id = :id"
|
||||
).setParameter( "id", 1L ).uniqueResult();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "Post")
|
||||
@Table(name = "post")
|
||||
public static class Post {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String title;
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
private List<PostComment> comments = new ArrayList<PostComment>();
|
||||
|
||||
@ManyToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(name = "post_tag",
|
||||
joinColumns = @JoinColumn(name = "post_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "tag_id")
|
||||
)
|
||||
private List<Tag> tags = new ArrayList<>();
|
||||
|
||||
public Post() {
|
||||
}
|
||||
|
||||
public Post(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Post(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public List<Tag> getTags() {
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "PostComment")
|
||||
@Table(name = "post_comment")
|
||||
public static class PostComment {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String review;
|
||||
|
||||
public PostComment() {
|
||||
}
|
||||
|
||||
public PostComment(String review) {
|
||||
this.review = review;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getReview() {
|
||||
return review;
|
||||
}
|
||||
|
||||
public void setReview(String review) {
|
||||
this.review = review;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Tag")
|
||||
@Table(name = "tag")
|
||||
public static class Tag {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
* 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.test.collection.bag;
|
||||
package org.hibernate.orm.test.collection.bag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -21,12 +21,11 @@ import org.hibernate.boot.Metadata;
|
|||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
|
||||
import org.hibernate.loader.MultipleBagFetchException;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class MultipleBagFetchTest {
|
||||
|
|
@ -1,195 +0,0 @@
|
|||
/*
|
||||
* 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.test.collection.bag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.loader.MultipleBagFetchException;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class MultipleBagFetchHqlTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
Post.class,
|
||||
PostComment.class,
|
||||
Tag.class
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleBagFetchHql() throws Exception {
|
||||
|
||||
Session session = openSession();
|
||||
Transaction transaction = session.beginTransaction();
|
||||
|
||||
Post post = new Post();
|
||||
post.setId( 1L );
|
||||
post.setTitle( String.format( "Post nr. %d", 1 ) );
|
||||
PostComment comment = new PostComment();
|
||||
comment.setId(1L);
|
||||
comment.setReview( "Excellent!" );
|
||||
session.persist(post);
|
||||
session.persist( comment );
|
||||
post.comments.add( comment );
|
||||
|
||||
transaction.commit();
|
||||
session.close();
|
||||
|
||||
|
||||
session = openSession();
|
||||
session.beginTransaction();
|
||||
|
||||
try {
|
||||
post = (Post) session.createQuery(
|
||||
"select p " +
|
||||
"from Post p " +
|
||||
"join fetch p.tags " +
|
||||
"join fetch p.comments " +
|
||||
"where p.id = :id"
|
||||
)
|
||||
.setParameter( "id", 1L )
|
||||
.uniqueResult();
|
||||
fail("Should throw org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags");
|
||||
}
|
||||
catch ( IllegalArgumentException expected ) {
|
||||
session.getTransaction().rollback();
|
||||
// MultipleBagFetchException was converted to IllegalArgumentException
|
||||
assertTrue( MultipleBagFetchException.class.isInstance( expected.getCause() ) );
|
||||
}
|
||||
finally {
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Post")
|
||||
@Table(name = "post")
|
||||
public static class Post {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String title;
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
private List<PostComment> comments = new ArrayList<PostComment>();
|
||||
|
||||
@ManyToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(name = "post_tag",
|
||||
joinColumns = @JoinColumn(name = "post_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "tag_id")
|
||||
)
|
||||
private List<Tag> tags = new ArrayList<Tag>();
|
||||
|
||||
public Post() {
|
||||
}
|
||||
|
||||
public Post(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Post(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public List<Tag> getTags() {
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "PostComment")
|
||||
@Table(name = "post_comment")
|
||||
public static class PostComment {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String review;
|
||||
|
||||
public PostComment() {
|
||||
}
|
||||
|
||||
public PostComment(String review) {
|
||||
this.review = review;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getReview() {
|
||||
return review;
|
||||
}
|
||||
|
||||
public void setReview(String review) {
|
||||
this.review = review;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Tag")
|
||||
@Table(name = "tag")
|
||||
public static class Tag {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue