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 java.util.function.Consumer;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
|
import org.hibernate.collection.spi.BagSemantics;
|
||||||
import org.hibernate.collection.spi.CollectionInitializerProducer;
|
import org.hibernate.collection.spi.CollectionInitializerProducer;
|
||||||
import org.hibernate.collection.spi.CollectionSemantics;
|
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchTiming;
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
|
||||||
import org.hibernate.sql.results.graph.Fetch;
|
import org.hibernate.sql.results.graph.Fetch;
|
||||||
import org.hibernate.sql.results.graph.collection.internal.BagInitializerProducer;
|
import org.hibernate.sql.results.graph.collection.internal.BagInitializerProducer;
|
||||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||||
|
@ -29,7 +28,7 @@ import org.hibernate.sql.results.graph.FetchParent;
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractBagSemantics<B extends Collection<?>> implements CollectionSemantics<B> {
|
public abstract class AbstractBagSemantics<B extends Collection<?>> implements BagSemantics<B> {
|
||||||
@Override
|
@Override
|
||||||
public Class<B> getCollectionJavaType() {
|
public Class<B> getCollectionJavaType() {
|
||||||
//noinspection unchecked
|
//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.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
|
import org.hibernate.collection.spi.BagSemantics;
|
||||||
import org.hibernate.engine.FetchStyle;
|
import org.hibernate.engine.FetchStyle;
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchTiming;
|
||||||
import org.hibernate.engine.profile.FetchProfile;
|
import org.hibernate.engine.profile.FetchProfile;
|
||||||
|
@ -27,6 +28,7 @@ import org.hibernate.graph.GraphSemantic;
|
||||||
import org.hibernate.graph.spi.RootGraphImplementor;
|
import org.hibernate.graph.spi.RootGraphImplementor;
|
||||||
import org.hibernate.internal.FilterHelper;
|
import org.hibernate.internal.FilterHelper;
|
||||||
import org.hibernate.internal.util.MutableInteger;
|
import org.hibernate.internal.util.MutableInteger;
|
||||||
|
import org.hibernate.loader.MultipleBagFetchException;
|
||||||
import org.hibernate.loader.ast.spi.Loadable;
|
import org.hibernate.loader.ast.spi.Loadable;
|
||||||
import org.hibernate.loader.ast.spi.Loader;
|
import org.hibernate.loader.ast.spi.Loader;
|
||||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
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.collection.CollectionPersister;
|
||||||
import org.hibernate.persister.entity.Joinable;
|
import org.hibernate.persister.entity.Joinable;
|
||||||
import org.hibernate.query.ComparisonOperator;
|
import org.hibernate.query.ComparisonOperator;
|
||||||
|
import org.hibernate.query.EntityIdentifierNavigablePath;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.ast.spi.SimpleFromClauseAccessImpl;
|
import org.hibernate.sql.ast.spi.SimpleFromClauseAccessImpl;
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasBaseManager;
|
import org.hibernate.sql.ast.spi.SqlAliasBaseManager;
|
||||||
|
@ -443,7 +446,8 @@ public class LoaderSelectBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Fetch> fetches = new ArrayList<>();
|
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();
|
final FetchableContainer referencedMappingContainer = fetchParent.getReferencedMappingContainer();
|
||||||
if ( fetchParent.getNavigablePath().getParent() != null ) {
|
if ( fetchParent.getNavigablePath().getParent() != null ) {
|
||||||
|
@ -452,6 +456,9 @@ public class LoaderSelectBuilder {
|
||||||
}
|
}
|
||||||
referencedMappingContainer.visitFetchables(
|
referencedMappingContainer.visitFetchables(
|
||||||
fetchable -> processor.accept( fetchable, false ), null );
|
fetchable -> processor.accept( fetchable, false ), null );
|
||||||
|
if ( bagRoles.size() > 1 ) {
|
||||||
|
throw new MultipleBagFetchException( bagRoles );
|
||||||
|
}
|
||||||
return fetches;
|
return fetches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -459,11 +466,12 @@ public class LoaderSelectBuilder {
|
||||||
FetchParent fetchParent,
|
FetchParent fetchParent,
|
||||||
QuerySpec querySpec,
|
QuerySpec querySpec,
|
||||||
LoaderSqlAstCreationState creationState,
|
LoaderSqlAstCreationState creationState,
|
||||||
List<Fetch> fetches) {
|
List<Fetch> fetches,
|
||||||
|
List<String> bagRoles) {
|
||||||
return (fetchable, isKeyFetchable) -> {
|
return (fetchable, isKeyFetchable) -> {
|
||||||
NavigablePath panrentNavigablePath = fetchParent.getNavigablePath();
|
NavigablePath panrentNavigablePath = fetchParent.getNavigablePath();
|
||||||
if ( isKeyFetchable ) {
|
if ( isKeyFetchable ) {
|
||||||
panrentNavigablePath = panrentNavigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME );
|
panrentNavigablePath = new EntityIdentifierNavigablePath( panrentNavigablePath );
|
||||||
}
|
}
|
||||||
final NavigablePath fetchablePath = panrentNavigablePath.append( fetchable.getFetchableName() );
|
final NavigablePath fetchablePath = panrentNavigablePath.append( fetchable.getFetchableName() );
|
||||||
|
|
||||||
|
@ -542,23 +550,31 @@ public class LoaderSelectBuilder {
|
||||||
null,
|
null,
|
||||||
creationState
|
creationState
|
||||||
);
|
);
|
||||||
fetches.add( fetch );
|
|
||||||
|
|
||||||
if ( fetchable instanceof PluralAttributeMapping && fetchTiming == FetchTiming.IMMEDIATE && joined ) {
|
if ( fetch.getTiming() == FetchTiming.IMMEDIATE && fetchable instanceof PluralAttributeMapping ) {
|
||||||
final TableGroup joinTableGroup = creationState.getFromClauseAccess().getTableGroup( fetchablePath );
|
|
||||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) fetchable;
|
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) fetchable;
|
||||||
applyFiltering(
|
if ( pluralAttributeMapping.getMappedTypeDescriptor()
|
||||||
querySpec,
|
.getCollectionSemantics() instanceof BagSemantics ) {
|
||||||
joinTableGroup,
|
bagRoles.add( fetchable.getNavigableRole().getNavigableName() );
|
||||||
pluralAttributeMapping
|
}
|
||||||
);
|
if ( joined ) {
|
||||||
applyOrdering(
|
final TableGroup joinTableGroup = creationState.getFromClauseAccess()
|
||||||
querySpec,
|
.getTableGroup( fetchablePath );
|
||||||
fetchablePath,
|
applyFiltering(
|
||||||
pluralAttributeMapping,
|
querySpec,
|
||||||
creationState
|
joinTableGroup,
|
||||||
);
|
pluralAttributeMapping
|
||||||
|
);
|
||||||
|
applyOrdering(
|
||||||
|
querySpec,
|
||||||
|
fetchablePath,
|
||||||
|
pluralAttributeMapping,
|
||||||
|
creationState
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetches.add( fetch );
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if ( changeFetchDepth ) {
|
if ( changeFetchDepth ) {
|
||||||
|
|
|
@ -48,7 +48,16 @@ public class SingleIdEntityLoaderStandardImpl<T> extends SingleIdEntityLoaderSup
|
||||||
public void prepare() {
|
public void prepare() {
|
||||||
// see `org.hibernate.persister.entity.AbstractEntityPersister#createLoaders`
|
// see `org.hibernate.persister.entity.AbstractEntityPersister#createLoaders`
|
||||||
// we should pre-load a few - maybe LockMode.NONE and LockMode.READ
|
// 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
|
@Override
|
||||||
|
|
|
@ -16,7 +16,7 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
*/
|
*/
|
||||||
public abstract class SingleIdEntityLoaderSupport<T> implements SingleIdEntityLoader<T> {
|
public abstract class SingleIdEntityLoaderSupport<T> implements SingleIdEntityLoader<T> {
|
||||||
private final EntityMappingType entityDescriptor;
|
private final EntityMappingType entityDescriptor;
|
||||||
private final SessionFactoryImplementor sessionFactory;
|
protected final SessionFactoryImplementor sessionFactory;
|
||||||
|
|
||||||
private DatabaseSnapshotExecutor databaseSnapshotExecutor;
|
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();
|
CollectionPart getIndexDescriptor();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
CollectionMappingType getMappedTypeDescriptor();
|
||||||
|
|
||||||
interface IndexMetadata {
|
interface IndexMetadata {
|
||||||
CollectionPart getIndexDescriptor();
|
CollectionPart getIndexDescriptor();
|
||||||
int getListIndexBase();
|
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.convert.spi.BasicValueConverter;
|
||||||
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.EntityIdentifierNavigablePath;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
|
@ -187,9 +188,14 @@ public class BasicValuedCollectionPart
|
||||||
nature.getName()
|
nature.getName()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
NavigablePath parentNavigablePath = fetchablePath.getParent();
|
||||||
|
if ( parentNavigablePath instanceof EntityIdentifierNavigablePath ) {
|
||||||
|
parentNavigablePath = parentNavigablePath.getParent();
|
||||||
|
}
|
||||||
|
|
||||||
final TableGroup tableGroup = creationState.getSqlAstCreationState()
|
final TableGroup tableGroup = creationState.getSqlAstCreationState()
|
||||||
.getFromClauseAccess()
|
.getFromClauseAccess()
|
||||||
.findTableGroup( fetchablePath.getParent() );
|
.findTableGroup( parentNavigablePath );
|
||||||
final SqlSelection sqlSelection = resolveSqlSelection( tableGroup, creationState );
|
final SqlSelection sqlSelection = resolveSqlSelection( tableGroup, creationState );
|
||||||
|
|
||||||
return new BasicFetch(
|
return new BasicFetch(
|
||||||
|
|
|
@ -6,102 +6,62 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.metamodel.mapping.internal;
|
package org.hibernate.metamodel.mapping.internal;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
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.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
import org.hibernate.metamodel.mapping.ColumnConsumer;
|
import org.hibernate.metamodel.internal.AbstractCompositeIdentifierMapping;
|
||||||
import org.hibernate.metamodel.mapping.CompositeIdentifierMapping;
|
|
||||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||||
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.MappingType;
|
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
|
||||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
|
||||||
import org.hibernate.property.access.spi.PropertyAccess;
|
import org.hibernate.property.access.spi.PropertyAccess;
|
||||||
import org.hibernate.proxy.HibernateProxy;
|
import org.hibernate.proxy.HibernateProxy;
|
||||||
import org.hibernate.query.NavigablePath;
|
|
||||||
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||||
import org.hibernate.sql.ast.Clause;
|
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.SqlAstCreationState;
|
||||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
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.TableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
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}
|
* Support for {@link javax.persistence.EmbeddedId}
|
||||||
*
|
*
|
||||||
* @author Andrea Boriero
|
* @author Andrea Boriero
|
||||||
*/
|
*/
|
||||||
public class EmbeddedIdentifierMappingImpl
|
public class EmbeddedIdentifierMappingImpl extends AbstractCompositeIdentifierMapping {
|
||||||
implements CompositeIdentifierMapping, EmbeddableValuedFetchable, FetchOptions {
|
|
||||||
private final NavigableRole navigableRole;
|
|
||||||
private final EntityMappingType entityMapping;
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final MappingType type;
|
|
||||||
private final StateArrayContributorMetadataAccess attributeMetadataAccess;
|
|
||||||
private final PropertyAccess propertyAccess;
|
private final PropertyAccess propertyAccess;
|
||||||
private final String tableExpression;
|
|
||||||
private final String[] attrColumnNames;
|
|
||||||
private final SessionFactoryImplementor sessionFactory;
|
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
public EmbeddedIdentifierMappingImpl(
|
public EmbeddedIdentifierMappingImpl(
|
||||||
EntityMappingType entityMapping,
|
EntityMappingType entityMapping,
|
||||||
String name,
|
String name,
|
||||||
MappingType type,
|
EmbeddableMappingType embeddableDescriptor,
|
||||||
StateArrayContributorMetadataAccess attributeMetadataAccess,
|
StateArrayContributorMetadataAccess attributeMetadataAccess,
|
||||||
PropertyAccess propertyAccess,
|
PropertyAccess propertyAccess,
|
||||||
String tableExpression,
|
String tableExpression,
|
||||||
String[] attrColumnNames,
|
String[] attrColumnNames,
|
||||||
SessionFactoryImplementor sessionFactory) {
|
SessionFactoryImplementor sessionFactory) {
|
||||||
this.navigableRole = entityMapping.getNavigableRole().appendContainer( EntityIdentifierMapping.ROLE_LOCAL_NAME );
|
super(
|
||||||
this.entityMapping = entityMapping;
|
attributeMetadataAccess,
|
||||||
|
embeddableDescriptor,
|
||||||
|
entityMapping,
|
||||||
|
tableExpression,
|
||||||
|
attrColumnNames,
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.type = type;
|
|
||||||
this.attributeMetadataAccess = attributeMetadataAccess;
|
|
||||||
this.propertyAccess = propertyAccess;
|
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
|
@Override
|
||||||
|
@ -109,11 +69,6 @@ public class EmbeddedIdentifierMappingImpl
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public NavigableRole getNavigableRole() {
|
|
||||||
return navigableRole;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getIdentifier(Object entity, SharedSessionContractImplementor session) {
|
public Object getIdentifier(Object entity, SharedSessionContractImplementor session) {
|
||||||
if ( entity instanceof HibernateProxy ) {
|
if ( entity instanceof HibernateProxy ) {
|
||||||
|
@ -127,44 +82,6 @@ public class EmbeddedIdentifierMappingImpl
|
||||||
propertyAccess.getSetter().set( entity, id, session.getFactory() );
|
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
|
@Override
|
||||||
public void visitJdbcValues(
|
public void visitJdbcValues(
|
||||||
Object value,
|
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
|
@Override
|
||||||
public Expression toSqlExpression(
|
public Expression toSqlExpression(
|
||||||
TableGroup tableGroup,
|
TableGroup tableGroup,
|
||||||
Clause clause,
|
Clause clause,
|
||||||
SqmToSqlAstConverter walker,
|
SqmToSqlAstConverter walker,
|
||||||
SqlAstCreationState sqlAstCreationState) {
|
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() );
|
final TableReference tableReference = tableGroup.resolveTableReference( getContainingTableExpression() );
|
||||||
getEmbeddableTypeDescriptor().visitJdbcTypes(
|
getEmbeddableTypeDescriptor().visitJdbcTypes(
|
||||||
new Consumer<JdbcMapping>() {
|
new Consumer<JdbcMapping>() {
|
||||||
|
@ -217,21 +121,22 @@ public class EmbeddedIdentifierMappingImpl
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(JdbcMapping jdbcMapping) {
|
public void accept(JdbcMapping jdbcMapping) {
|
||||||
final String attrColumnExpr = attrColumnNames[ index++ ];
|
final String attrColumnExpr = attrColumnNames.get( index++ );
|
||||||
|
|
||||||
final Expression columnReference = sqlAstCreationState.getSqlExpressionResolver().resolveSqlExpression(
|
final Expression columnReference = sqlAstCreationState.getSqlExpressionResolver()
|
||||||
SqlExpressionResolver.createColumnReferenceKey(
|
.resolveSqlExpression(
|
||||||
tableReference,
|
SqlExpressionResolver.createColumnReferenceKey(
|
||||||
attrColumnExpr
|
tableReference,
|
||||||
),
|
attrColumnExpr
|
||||||
sqlAstProcessingState -> new ColumnReference(
|
),
|
||||||
tableReference.getIdentificationVariable(),
|
sqlAstProcessingState -> new ColumnReference(
|
||||||
attrColumnExpr,
|
tableReference.getIdentificationVariable(),
|
||||||
false,
|
attrColumnExpr,
|
||||||
jdbcMapping,
|
false,
|
||||||
sqlAstCreationState.getCreationContext().getSessionFactory()
|
jdbcMapping,
|
||||||
)
|
sqlAstCreationState.getCreationContext().getSessionFactory()
|
||||||
);
|
)
|
||||||
|
);
|
||||||
|
|
||||||
columnReferences.add( (ColumnReference) columnReference );
|
columnReferences.add( (ColumnReference) columnReference );
|
||||||
}
|
}
|
||||||
|
@ -243,99 +148,23 @@ public class EmbeddedIdentifierMappingImpl
|
||||||
return new SqlTuple( columnReferences, this );
|
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
|
@Override
|
||||||
public String getSqlAliasStem() {
|
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
|
@Override
|
||||||
public String getFetchableName() {
|
public String getFetchableName() {
|
||||||
return name;
|
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
|
@Override
|
||||||
public int getNumberOfFetchables() {
|
public int getNumberOfFetchables() {
|
||||||
return getEmbeddableTypeDescriptor().getNumberOfAttributeMappings();
|
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
|
@Override
|
||||||
public int getAttributeCount() {
|
public int getAttributeCount() {
|
||||||
|
@ -348,13 +177,4 @@ public class EmbeddedIdentifierMappingImpl
|
||||||
return (Collection) getEmbeddableTypeDescriptor().getAttributeMappings();
|
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;
|
package org.hibernate.metamodel.mapping.internal;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
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.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.mapping.Component;
|
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.EmbeddableMappingType;
|
||||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
|
||||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
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.query.sqm.sql.SqmToSqlAstConverter;
|
||||||
import org.hibernate.sql.ast.Clause;
|
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.SqlAstCreationState;
|
||||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
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.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.
|
* A "non-aggregated" composite identifier.
|
||||||
*
|
* <p>
|
||||||
* This is an identifier mapped using JPA's {@link javax.persistence.MapsId} feature.
|
* 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
|
* @author Steve Ebersole
|
||||||
|
* @apiNote Technically a MapsId id does not have to be composite; we still handle that this class however
|
||||||
*/
|
*/
|
||||||
public class NonAggregatedIdentifierMappingImpl
|
public class NonAggregatedIdentifierMappingImpl extends AbstractCompositeIdentifierMapping {
|
||||||
implements CompositeIdentifierMapping, EmbeddableValuedFetchable, FetchOptions {
|
|
||||||
private final EmbeddableMappingType embeddableDescriptor;
|
|
||||||
private final NavigableRole navigableRole;
|
|
||||||
private final EntityMappingType entityMapping;
|
|
||||||
|
|
||||||
private final List<SingularAttributeMapping> idAttributeMappings;
|
private final List<SingularAttributeMapping> idAttributeMappings;
|
||||||
|
|
||||||
private final StateArrayContributorMetadataAccess attributeMetadataAccess;
|
|
||||||
private final String rootTableName;
|
|
||||||
private final List<String> idColumnNames;
|
|
||||||
|
|
||||||
public NonAggregatedIdentifierMappingImpl(
|
public NonAggregatedIdentifierMappingImpl(
|
||||||
EmbeddableMappingType embeddableDescriptor,
|
EmbeddableMappingType embeddableDescriptor,
|
||||||
EntityMappingType entityMapping,
|
EntityMappingType entityMapping,
|
||||||
|
@ -78,35 +45,15 @@ public class NonAggregatedIdentifierMappingImpl
|
||||||
Component bootIdClassDescriptor,
|
Component bootIdClassDescriptor,
|
||||||
MappingModelCreationProcess creationProcess) {
|
MappingModelCreationProcess creationProcess) {
|
||||||
// todo (6.0) : handle MapsId and IdClass
|
// todo (6.0) : handle MapsId and IdClass
|
||||||
|
super(
|
||||||
this.embeddableDescriptor = embeddableDescriptor;
|
attributeMetadataAccess,
|
||||||
this.entityMapping = entityMapping;
|
embeddableDescriptor,
|
||||||
|
entityMapping,
|
||||||
|
rootTableName,
|
||||||
|
rootTableKeyColumnNames,
|
||||||
|
creationProcess.getCreationContext().getSessionFactory()
|
||||||
|
);
|
||||||
this.idAttributeMappings = idAttributeMappings;
|
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
|
@Override
|
||||||
|
@ -128,56 +75,9 @@ public class NonAggregatedIdentifierMappingImpl
|
||||||
public void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session) {
|
public void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session) {
|
||||||
// nothing to do
|
// 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
|
// 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
|
@Override
|
||||||
public Expression toSqlExpression(
|
public Expression toSqlExpression(
|
||||||
TableGroup tableGroup,
|
TableGroup tableGroup,
|
||||||
|
@ -187,85 +87,19 @@ public class NonAggregatedIdentifierMappingImpl
|
||||||
return null;
|
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
|
@Override
|
||||||
public String getSqlAliasStem() {
|
public String getSqlAliasStem() {
|
||||||
return "id";
|
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
|
@Override
|
||||||
public String getFetchableName() {
|
public String getFetchableName() {
|
||||||
return "id";
|
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
|
@Override
|
||||||
public int getNumberOfFetchables() {
|
public int getNumberOfFetchables() {
|
||||||
return idAttributeMappings.size();
|
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.metamodel.model.domain.NavigableRole;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.property.access.spi.PropertyAccess;
|
import org.hibernate.property.access.spi.PropertyAccess;
|
||||||
|
import org.hibernate.query.EntityIdentifierNavigablePath;
|
||||||
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.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.EntityFetchJoinedImpl;
|
||||||
import org.hibernate.sql.results.graph.entity.internal.EntityFetchSelectImpl;
|
import org.hibernate.sql.results.graph.entity.internal.EntityFetchSelectImpl;
|
||||||
import org.hibernate.sql.results.graph.entity.internal.EntityResultJoinedSubclassImpl;
|
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.CircularBiDirectionalFetchImpl;
|
||||||
|
import org.hibernate.sql.results.internal.domain.CircularFetchImpl;
|
||||||
import org.hibernate.type.ForeignKeyDirection;
|
import org.hibernate.type.ForeignKeyDirection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -227,7 +228,7 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
||||||
NavigablePath parentNavigablePath = fetchablePath.getParent();
|
NavigablePath parentNavigablePath = fetchablePath.getParent();
|
||||||
ModelPart modelPart = creationState.resolveModelPart( parentNavigablePath );
|
ModelPart modelPart = creationState.resolveModelPart( parentNavigablePath );
|
||||||
if ( modelPart instanceof EmbeddedIdentifierMappingImpl ) {
|
if ( modelPart instanceof EmbeddedIdentifierMappingImpl ) {
|
||||||
while ( parentNavigablePath.getFullPath().endsWith( EntityIdentifierMapping.ROLE_LOCAL_NAME ) ) {
|
while ( parentNavigablePath instanceof EntityIdentifierNavigablePath ) {
|
||||||
parentNavigablePath = parentNavigablePath.getParent();
|
parentNavigablePath = parentNavigablePath.getParent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -340,7 +341,7 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( modelPart instanceof EntityCollectionPart ) {
|
if ( modelPart instanceof EntityCollectionPart ) {
|
||||||
if ( parentOfParent.getFullPath().endsWith( EntityIdentifierMapping.ROLE_LOCAL_NAME ) ) {
|
if ( parentOfParent instanceof EntityIdentifierNavigablePath ) {
|
||||||
parentOfParent = parentOfParent.getParent();
|
parentOfParent = parentOfParent.getParent();
|
||||||
}
|
}
|
||||||
return ( (PluralAttributeMapping) creationState.resolveModelPart( parentOfParent ) ).isBidirectionalAttributeName(
|
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.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
import org.hibernate.NotYetImplementedFor6Exception;
|
||||||
|
import org.hibernate.collection.spi.BagSemantics;
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchTiming;
|
||||||
import org.hibernate.engine.profile.FetchProfile;
|
import org.hibernate.engine.profile.FetchProfile;
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
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.CollectionHelper;
|
||||||
import org.hibernate.internal.util.collections.Stack;
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
import org.hibernate.internal.util.collections.StandardStack;
|
import org.hibernate.internal.util.collections.StandardStack;
|
||||||
|
import org.hibernate.loader.MultipleBagFetchException;
|
||||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
|
@ -271,6 +273,7 @@ public class StandardSqmSelectTranslator
|
||||||
@Override
|
@Override
|
||||||
public List<Fetch> visitFetches(FetchParent fetchParent) {
|
public List<Fetch> visitFetches(FetchParent fetchParent) {
|
||||||
final List<Fetch> fetches = CollectionHelper.arrayList( fetchParent.getReferencedMappingType().getNumberOfFetchables() );
|
final List<Fetch> fetches = CollectionHelper.arrayList( fetchParent.getReferencedMappingType().getNumberOfFetchables() );
|
||||||
|
final List<String> bagRoles = new ArrayList<>();
|
||||||
|
|
||||||
final BiConsumer<Fetchable, Boolean> fetchableBiConsumer = (fetchable, isKeyFetchable) -> {
|
final BiConsumer<Fetchable, Boolean> fetchableBiConsumer = (fetchable, isKeyFetchable) -> {
|
||||||
final NavigablePath fetchablePath = fetchParent.getNavigablePath().append( fetchable.getFetchableName() );
|
final NavigablePath fetchablePath = fetchParent.getNavigablePath().append( fetchable.getFetchableName() );
|
||||||
|
@ -291,6 +294,12 @@ public class StandardSqmSelectTranslator
|
||||||
final Fetch fetch = buildFetch( fetchablePath, fetchParent, fetchable, isKeyFetchable );
|
final Fetch fetch = buildFetch( fetchablePath, fetchParent, fetchable, isKeyFetchable );
|
||||||
|
|
||||||
if ( fetch != null ) {
|
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 );
|
fetches.add( fetch );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,7 +313,9 @@ public class StandardSqmSelectTranslator
|
||||||
// fetchParent.getReferencedMappingContainer().visitFetchables( fetchableBiConsumer, treatTargetType );
|
// fetchParent.getReferencedMappingContainer().visitFetchables( fetchableBiConsumer, treatTargetType );
|
||||||
fetchParent.getReferencedMappingContainer().visitKeyFetchables( fetchable -> fetchableBiConsumer.accept( fetchable, true ), null );
|
fetchParent.getReferencedMappingContainer().visitKeyFetchables( fetchable -> fetchableBiConsumer.accept( fetchable, true ), null );
|
||||||
fetchParent.getReferencedMappingContainer().visitFetchables( fetchable -> fetchableBiConsumer.accept( fetchable, false ), null );
|
fetchParent.getReferencedMappingContainer().visitFetchables( fetchable -> fetchableBiConsumer.accept( fetchable, false ), null );
|
||||||
|
if ( bagRoles.size() > 1 ) {
|
||||||
|
throw new MultipleBagFetchException( bagRoles );
|
||||||
|
}
|
||||||
return fetches;
|
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.SingleAttributeIdentifierMapping;
|
||||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||||
import org.hibernate.persister.entity.AbstractEntityPersister;
|
import org.hibernate.persister.entity.AbstractEntityPersister;
|
||||||
|
import org.hibernate.query.EntityIdentifierNavigablePath;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
import org.hibernate.sql.results.graph.AbstractFetchParent;
|
import org.hibernate.sql.results.graph.AbstractFetchParent;
|
||||||
|
@ -71,7 +72,7 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent
|
||||||
identifierResult = null;
|
identifierResult = null;
|
||||||
if ( identifierMapping instanceof SingleAttributeIdentifierMapping ) {
|
if ( identifierMapping instanceof SingleAttributeIdentifierMapping ) {
|
||||||
identifierMapping.createDomainResult(
|
identifierMapping.createDomainResult(
|
||||||
navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ),
|
new EntityIdentifierNavigablePath( navigablePath ),
|
||||||
entityTableGroup,
|
entityTableGroup,
|
||||||
null,
|
null,
|
||||||
creationState
|
creationState
|
||||||
|
@ -83,7 +84,7 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
identifierResult = identifierMapping.createDomainResult(
|
identifierResult = identifierMapping.createDomainResult(
|
||||||
navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ),
|
new EntityIdentifierNavigablePath( navigablePath ),
|
||||||
entityTableGroup,
|
entityTableGroup,
|
||||||
null,
|
null,
|
||||||
creationState
|
creationState
|
||||||
|
@ -141,7 +142,7 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent
|
||||||
attributeMapping -> {
|
attributeMapping -> {
|
||||||
if ( attributeMapping instanceof ToOneAttributeMapping ) {
|
if ( attributeMapping instanceof ToOneAttributeMapping ) {
|
||||||
( (ToOneAttributeMapping) attributeMapping ).getForeignKeyDescriptor().createDomainResult(
|
( (ToOneAttributeMapping) attributeMapping ).getForeignKeyDescriptor().createDomainResult(
|
||||||
navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ),
|
new EntityIdentifierNavigablePath( navigablePath ),
|
||||||
entityTableGroup,
|
entityTableGroup,
|
||||||
null,
|
null,
|
||||||
creationState
|
creationState
|
||||||
|
@ -149,7 +150,7 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
attributeMapping.createDomainResult(
|
attributeMapping.createDomainResult(
|
||||||
navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ),
|
new EntityIdentifierNavigablePath( navigablePath ),
|
||||||
entityTableGroup,
|
entityTableGroup,
|
||||||
null,
|
null,
|
||||||
creationState
|
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.
|
* 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>.
|
* 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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -21,12 +21,11 @@ import org.hibernate.boot.Metadata;
|
||||||
import org.hibernate.boot.MetadataSources;
|
import org.hibernate.boot.MetadataSources;
|
||||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||||
|
|
||||||
import org.hibernate.loader.MultipleBagFetchException;
|
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 {
|
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