Implement Composite Foreign Key for EmbeddedId

This commit is contained in:
Andrea Boriero 2020-03-12 14:27:06 +00:00 committed by Steve Ebersole
parent 903bb292e7
commit 7f4c25095e
30 changed files with 2224 additions and 207 deletions

View File

@ -98,7 +98,6 @@ class DatabaseSnapshotExecutor {
); );
final List<DomainResult> domainResults = new ArrayList<>(); final List<DomainResult> domainResults = new ArrayList<>();
final NavigablePath idPath = rootPath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME );
entityDescriptor.getIdentifierMapping().visitColumns( entityDescriptor.getIdentifierMapping().visitColumns(
(tab, col, jdbcMapping) -> { (tab, col, jdbcMapping) -> {
final TableReference tableReference = rootTableGroup.resolveTableReference( tab ); final TableReference tableReference = rootTableGroup.resolveTableReference( tab );

View File

@ -28,6 +28,7 @@ import org.hibernate.internal.FilterHelper.TransformResult;
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;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
@ -477,8 +478,11 @@ public class LoaderSelectBuilder {
LoaderSqlAstCreationState creationState, LoaderSqlAstCreationState creationState,
List<Fetch> fetches) { List<Fetch> fetches) {
return (fetchable, isKeyFetchable) -> { return (fetchable, isKeyFetchable) -> {
NavigablePath navigablePath = fetchParent.getNavigablePath();
final NavigablePath fetchablePath = fetchParent.getNavigablePath().append( fetchable.getFetchableName() ); if ( isKeyFetchable ) {
navigablePath = navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME );
}
final NavigablePath fetchablePath = navigablePath.append( fetchable.getFetchableName() );
final Fetch biDirectionalFetch = fetchable.resolveCircularFetch( final Fetch biDirectionalFetch = fetchable.resolveCircularFetch(
fetchablePath, fetchablePath,

View File

@ -155,7 +155,9 @@ public class LoaderSqlAstCreationState
if ( tableGroup.getNavigablePath().equals( navigablePath ) ) { if ( tableGroup.getNavigablePath().equals( navigablePath ) ) {
return tableGroup; return tableGroup;
} }
if(tableGroup.getNavigablePath().getIdentifierForTableGroup().equals( navigablePath.getIdentifierForTableGroup() )){ if ( tableGroup.getNavigablePath()
.getIdentifierForTableGroup()
.equals( navigablePath.getIdentifierForTableGroup() ) ) {
return tableGroup; return tableGroup;
} }

View File

@ -19,6 +19,7 @@ import org.hibernate.loader.internal.AliasConstantsHelper;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.query.sqm.function.SqmFunctionRegistry; import org.hibernate.query.sqm.function.SqmFunctionRegistry;
import org.hibernate.sql.Template; import org.hibernate.sql.Template;
import org.hibernate.type.ComponentType;
import org.hibernate.type.EntityType; import org.hibernate.type.EntityType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
@ -245,7 +246,10 @@ public class Column implements Selectable, Serializable, Cloneable {
&& size.getScale() == null && size.getPrecision() == null ) { && size.getScale() == null && size.getPrecision() == null ) {
if ( type instanceof EntityType ) { if ( type instanceof EntityType ) {
//ManyToOneType doesn't implement JdbcMapping //ManyToOneType doesn't implement JdbcMapping
type = mapping.getIdentifierType( ((EntityType) type).getAssociatedEntityName() ); type = mapping.getIdentifierType( ( (EntityType) type ).getAssociatedEntityName() );
if ( type instanceof ComponentType ) {
type = ( (ComponentType) type ).getSubtypes()[getTypeIndex()];
}
} }
if ( type instanceof JdbcMapping ) { if ( type instanceof JdbcMapping ) {
size = dialect.getDefaultSizeStrategy().resolveDefaultSize( size = dialect.getDefaultSizeStrategy().resolveDefaultSize(

View File

@ -318,16 +318,18 @@ public class EmbeddableMappingType implements ManagedMappingType {
@Override @Override
public Object disassemble(Object value, SharedSessionContractImplementor session) { public Object disassemble(Object value, SharedSessionContractImplementor session) {
throw new NotYetImplementedFor6Exception( getClass() ); final Collection<AttributeMapping> attributeMappings = getAttributeMappings();
}
@Override Object[] result = new Object[attributeMappings.size()];
public void visitDisassembledJdbcValues( int i = 0;
Object value, final Iterator<AttributeMapping> iterator = attributeMappings.iterator();
Clause clause, while ( iterator.hasNext() ) {
JdbcValuesConsumer valuesConsumer, AttributeMapping mapping = iterator.next();
SharedSessionContractImplementor session) { Object o = mapping.getPropertyAccess().getGetter().get( value );
throw new NotYetImplementedFor6Exception( getClass() ); result[i] = mapping.disassemble( o, session );
i++;
}
return result;
} }
@Override @Override
@ -344,6 +346,23 @@ public class EmbeddableMappingType implements ManagedMappingType {
); );
} }
@Override
public void visitDisassembledJdbcValues(
Object value,
Clause clause,
JdbcValuesConsumer valuesConsumer,
SharedSessionContractImplementor session) {
final Collection<AttributeMapping> attributeMappings = getAttributeMappings();
final Iterator<AttributeMapping> iterator = attributeMappings.iterator();
final Object[] values = (Object[]) value;
int i = 0;
while ( iterator.hasNext() ) {
AttributeMapping mapping = iterator.next();
mapping.visitDisassembledJdbcValues( values[i], clause, valuesConsumer, session );
i++;
}
}
public void visitColumns(ColumnConsumer consumer) { public void visitColumns(ColumnConsumer consumer) {
attributeMappings.values().forEach( attributeMappings.values().forEach(
attributeMapping -> attributeMapping.visitColumns( consumer ) attributeMapping -> attributeMapping.visitColumns( consumer )

View File

@ -68,7 +68,7 @@ public interface EntityValuedModelPart extends FetchableContainer {
Consumer<JdbcMapping> action, Consumer<JdbcMapping> action,
Clause clause, Clause clause,
TypeConfiguration typeConfiguration) { TypeConfiguration typeConfiguration) {
getEntityMappingType().getJdbcTypeCount( typeConfiguration ); getEntityMappingType().visitJdbcTypes( action, clause, typeConfiguration );
} }
@Override @Override

View File

@ -25,7 +25,7 @@ public interface ForeignKeyDescriptor extends VirtualModelPart {
ForeignKeyDirection getDirection(); ForeignKeyDirection getDirection();
DomainResult createCollectionFecthDomainResult( DomainResult createCollectionFetchDomainResult(
NavigablePath collectionPath, NavigablePath collectionPath,
TableGroup tableGroup, TableGroup tableGroup,
DomainResultCreationState creationState); DomainResultCreationState creationState);
@ -54,6 +54,10 @@ public interface ForeignKeyDescriptor extends VirtualModelPart {
return PART_NAME; return PART_NAME;
} }
String getReferringTableExpression();
String getTargetTableExpression();
/** /**
* Visits the FK "referring" columns * Visits the FK "referring" columns
*/ */
@ -62,36 +66,10 @@ public interface ForeignKeyDescriptor extends VirtualModelPart {
visitReferringColumns( consumer ); visitReferringColumns( consumer );
} }
String getReferringTableExpression();
void visitReferringColumns(ColumnConsumer consumer); void visitReferringColumns(ColumnConsumer consumer);
String getTargetTableExpression();
void visitTargetColumns(ColumnConsumer consumer); void visitTargetColumns(ColumnConsumer consumer);
void visitColumnMappings(FkColumnMappingConsumer consumer);
<T> T visitColumnMapping(FkColumnMappingFunction<T> function);
// todo (6.0): the 2 interfaces does not take into account composite keys, referringColumn
// and targetColumn should be collections
interface FkColumnMappingConsumer {
void consume(
String referringTable,
String referringColumn,
String targetTable,
String targetColumn,
JdbcMapping jdbcMapping);
}
interface FkColumnMappingFunction<T> {
T apply(
String referringTable,
String referringColumn,
String targetTable,
String targetColumn,
JdbcMapping jdbcMapping);
}
boolean areTargetColumnNamesEqualsTo(String[] columnNames);
} }

View File

@ -0,0 +1,571 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.metamodel.mapping.internal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.ColumnConsumer;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
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.query.ComparisonOperator;
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.spi.SqlSelection;
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.ast.tree.from.TableReferenceJoin;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.Junction;
import org.hibernate.sql.ast.tree.predicate.Predicate;
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.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.EmbeddableForeignKeyResultImpl;
import org.hibernate.type.ForeignKeyDirection;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* @author Andrea Boriero
*/
public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor, EmbeddableValuedFetchable {
private String name;
private StateArrayContributorMetadataAccess attributeMetadataAccess;
private final String keyColumnContainingTable;
private final List<String> keyColumnExpressions;
private final String targetColumnContainingTable;
private final List<String> targetColumnExpressions;
private final EmbeddableValuedModelPart mappingType;
private final List<JdbcMapping> jdbcMappings;
private final ForeignKeyDirection fKeyDirection;
private final int hasCode;
public EmbeddedForeignKeyDescriptor(
String attributeName,
EmbeddedIdentifierMappingImpl mappingType,
StateArrayContributorMetadataAccess attributeMetadataAccess,
ForeignKeyDirection fKeyDirection,
String keyColumnContainingTable,
List<String> keyColumnExpressions,
String targetColumnContainingTable,
List<String> targetColumnExpressions,
MappingModelCreationProcess creationProcess) {
name = attributeName;
this.attributeMetadataAccess = attributeMetadataAccess;
this.keyColumnContainingTable = keyColumnContainingTable;
this.keyColumnExpressions = keyColumnExpressions;
this.targetColumnContainingTable = targetColumnContainingTable;
this.targetColumnExpressions = targetColumnExpressions;
this.mappingType = mappingType;
this.fKeyDirection = fKeyDirection;
jdbcMappings = new ArrayList<>();
mappingType.getAttributes().forEach(
attribute -> {
final TypeConfiguration typeConfiguration = creationProcess.getCreationContext()
.getTypeConfiguration();
if ( attribute instanceof SingularAssociationAttributeMapping ) {
SingularAssociationAttributeMapping associationAttributeMapping = (SingularAssociationAttributeMapping) attribute;
associationAttributeMapping.getAssociatedEntityMappingType()
.getEntityPersister()
.getIdentifierMapping()
.visitJdbcTypes(
jdbcMapping ->
jdbcMappings.add( jdbcMapping )
,
null,
typeConfiguration
);
}
else {
attribute.visitJdbcTypes(
jdbcMapping -> {
jdbcMappings.add( jdbcMapping );
},
null,
typeConfiguration
);
}
}
);
this.hasCode = Objects.hash(
keyColumnContainingTable,
keyColumnExpressions,
targetColumnContainingTable,
targetColumnExpressions
);
}
@Override
public ForeignKeyDirection getDirection() {
return fKeyDirection;
}
@Override
public DomainResult createCollectionFetchDomainResult(
NavigablePath collectionPath,
TableGroup tableGroup,
DomainResultCreationState creationState) {
if ( targetColumnContainingTable.equals( keyColumnContainingTable ) ) {
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver();
final TableReference tableReference = tableGroup.resolveTableReference( keyColumnContainingTable );
final String identificationVariable = tableReference.getIdentificationVariable();
List<SqlSelection> sqlSelections = new ArrayList<>();
for ( int i = 0; i < keyColumnExpressions.size(); i++ ) {
final JdbcMapping jdbcMapping = jdbcMappings.get( i );
final String columnExpression = targetColumnExpressions.get( i );
final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection(
sqlExpressionResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey(
tableReference,
columnExpression
),
s ->
new ColumnReference(
identificationVariable,
columnExpression,
jdbcMapping,
creationState.getSqlAstCreationState()
.getCreationContext()
.getSessionFactory()
)
),
jdbcMapping.getJavaTypeDescriptor(),
sqlAstCreationState.getCreationContext().getDomainModel().getTypeConfiguration()
);
sqlSelections.add( sqlSelection );
}
return new EmbeddableForeignKeyResultImpl(
sqlSelections,
collectionPath,
mappingType,
null,
creationState
);
}
else {
return createDomainResult( collectionPath, tableGroup, creationState );
}
}
@Override
public DomainResult createDomainResult(
NavigablePath collectionPath,
TableGroup tableGroup,
DomainResultCreationState creationState) {
//noinspection unchecked
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver();
final TableReference tableReference = tableGroup.resolveTableReference( keyColumnContainingTable );
final String identificationVariable = tableReference.getIdentificationVariable();
int size = keyColumnExpressions.size();
List<SqlSelection> sqlSelections = new ArrayList<>(size);
for ( int i = 0; i < size; i++ ) {
final String columnExpression = keyColumnExpressions.get( i );
final JdbcMapping jdbcMapping = jdbcMappings.get( i );
final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection(
sqlExpressionResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey(
tableReference,
columnExpression
),
s ->
new ColumnReference(
identificationVariable,
columnExpression,
jdbcMapping,
creationState.getSqlAstCreationState()
.getCreationContext()
.getSessionFactory()
)
),
jdbcMapping.getJavaTypeDescriptor(),
sqlAstCreationState.getCreationContext().getDomainModel().getTypeConfiguration()
);
sqlSelections.add( sqlSelection );
}
return new EmbeddableForeignKeyResultImpl(
sqlSelections,
collectionPath,
mappingType,
null,
creationState
);
}
@Override
public Predicate generateJoinPredicate(
TableGroup lhs,
TableGroup tableGroup,
SqlAstJoinType sqlAstJoinType,
SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) {
TableReference lhsTableReference;
TableReference rhsTableKeyReference;
if ( targetColumnContainingTable.equals( keyColumnContainingTable ) ) {
lhsTableReference = getTableReferenceWhenTargetEqualsKey( lhs, tableGroup, keyColumnContainingTable );
rhsTableKeyReference = getTableReference(
lhs,
tableGroup,
targetColumnContainingTable
);
}
else {
lhsTableReference = getTableReference( lhs, tableGroup, keyColumnContainingTable );
rhsTableKeyReference = getTableReference(
lhs,
tableGroup,
targetColumnContainingTable
);
}
return generateJoinPredicate(
lhsTableReference,
rhsTableKeyReference,
sqlAstJoinType,
sqlExpressionResolver,
creationContext
);
}
@Override
public Predicate generateJoinPredicate(
TableReference lhs,
TableReference rhs,
SqlAstJoinType sqlAstJoinType,
SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) {
final String rhsTableExpression = rhs.getTableExpression();
final String lhsTableExpression = lhs.getTableExpression();
if ( lhsTableExpression.equals( keyColumnContainingTable ) ) {
assert rhsTableExpression.equals( targetColumnContainingTable );
return getPredicate( lhs, rhs, creationContext, keyColumnExpressions, targetColumnExpressions );
}
else {
assert rhsTableExpression.equals( keyColumnContainingTable );
return getPredicate( lhs, rhs, creationContext, targetColumnExpressions, keyColumnExpressions );
}
}
private Predicate getPredicate(
TableReference lhs,
TableReference rhs,
SqlAstCreationContext creationContext,
List<String> lhsExpressions,
List<String> rhsColumnExpressions) {
final Junction predicate = new Junction( Junction.Nature.CONJUNCTION );
for ( int i = 0; i < lhsExpressions.size(); i++ ) {
JdbcMapping jdbcMapping = jdbcMappings.get( i );
ComparisonPredicate comparisonPredicate =
new ComparisonPredicate(
new ColumnReference(
lhs,
lhsExpressions.get( i ),
jdbcMapping,
creationContext.getSessionFactory()
),
ComparisonOperator.EQUAL,
new ColumnReference(
rhs,
rhsColumnExpressions.get( i ),
jdbcMapping,
creationContext.getSessionFactory()
)
);
predicate.add( comparisonPredicate );
}
return predicate;
}
protected TableReference getTableReferenceWhenTargetEqualsKey(TableGroup lhs, TableGroup tableGroup, String table) {
if ( tableGroup.getPrimaryTableReference().getTableExpression().equals( table ) ) {
return tableGroup.getPrimaryTableReference();
}
if ( lhs.getPrimaryTableReference().getTableExpression().equals( table ) ) {
return lhs.getPrimaryTableReference();
}
for ( TableReferenceJoin tableJoin : lhs.getTableReferenceJoins() ) {
if ( tableJoin.getJoinedTableReference().getTableExpression().equals( table ) ) {
return tableJoin.getJoinedTableReference();
}
}
throw new IllegalStateException( "Could not resolve binding for table `" + table + "`" );
}
protected TableReference getTableReference(TableGroup lhs, TableGroup tableGroup, String table) {
if ( lhs.getPrimaryTableReference().getTableExpression().equals( table ) ) {
return lhs.getPrimaryTableReference();
}
else if ( tableGroup.getPrimaryTableReference().getTableExpression().equals( table ) ) {
return tableGroup.getPrimaryTableReference();
}
final TableReference tableReference = lhs.resolveTableReference( table );
if ( tableReference != null ) {
return tableReference;
}
throw new IllegalStateException( "Could not resolve binding for table `" + table + "`" );
}
@Override
public String getReferringTableExpression() {
return keyColumnContainingTable;
}
@Override
public String getTargetTableExpression() {
return targetColumnContainingTable;
}
@Override
public void visitReferringColumns(ColumnConsumer consumer) {
for ( int i = 0; i < keyColumnExpressions.size(); i++ ) {
consumer.accept( keyColumnContainingTable, keyColumnExpressions.get( i ), jdbcMappings.get( i ) );
}
}
@Override
public void visitTargetColumns(ColumnConsumer consumer) {
for ( int i = 0; i < keyColumnExpressions.size(); i++ ) {
consumer.accept( targetColumnContainingTable, targetColumnExpressions.get( i ), jdbcMappings.get( i ) );
}
}
@Override
public boolean areTargetColumnNamesEqualsTo(String[] columnNames) {
int length = columnNames.length;
if ( length != targetColumnExpressions.size() ) {
return false;
}
for ( int i = 0; i < length; i++ ) {
if ( !targetColumnExpressions.contains( columnNames[i] ) ) {
return false;
}
}
return true;
}
@Override
public MappingType getPartMappingType() {
throw new HibernateException( "Unexpected call to SimpleForeignKeyDescriptor#getPartMappingType" );
}
@Override
public JavaTypeDescriptor getJavaTypeDescriptor() {
return mappingType.getJavaTypeDescriptor();
}
@Override
public NavigableRole getNavigableRole() {
throw new UnsupportedOperationException();
}
@Override
public EntityMappingType findContainingEntityMapping() {
throw new UnsupportedOperationException();
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
EmbeddedForeignKeyDescriptor that = (EmbeddedForeignKeyDescriptor) o;
return keyColumnContainingTable.equals( that.keyColumnContainingTable ) &&
keyColumnExpressions.equals( that.keyColumnExpressions ) &&
targetColumnContainingTable.equals( that.targetColumnContainingTable ) &&
targetColumnExpressions.equals( that.targetColumnExpressions );
}
@Override
public int hashCode() {
return hasCode;
}
@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
);
lhs.addTableGroupJoin( new TableGroupJoin( navigablePath, SqlAstJoinType.INNER, compositeTableGroup, null ) );
return new TableGroupJoin(
navigablePath,
sqlAstJoinType,
compositeTableGroup
);
}
@Override
public EmbeddableMappingType getEmbeddableTypeDescriptor() {
return (EmbeddableMappingType) mappingType;
}
@Override
public String getContainingTableExpression() {
return keyColumnContainingTable;
}
@Override
public List<String> getMappedColumnExpressions() {
return keyColumnExpressions;
}
@Override
public SingularAttributeMapping getParentInjectionAttributeMapping() {
return null;
}
@Override
public Expression toSqlExpression(
TableGroup tableGroup,
Clause clause,
SqmToSqlAstConverter walker,
SqlAstCreationState sqlAstCreationState) {
final List<ColumnReference> columnReferences = CollectionHelper.arrayList( keyColumnExpressions.size() );
final TableReference tableReference = tableGroup.resolveTableReference( getContainingTableExpression() );
getEmbeddableTypeDescriptor().visitJdbcTypes(
new Consumer<JdbcMapping>() {
private int index = 0;
@Override
public void accept(JdbcMapping jdbcMapping) {
final String attrColumnExpr = keyColumnExpressions.get( index++ );
final Expression columnReference = sqlAstCreationState.getSqlExpressionResolver().resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey(
tableReference,
attrColumnExpr
),
sqlAstProcessingState -> new ColumnReference(
tableReference.getIdentificationVariable(),
attrColumnExpr,
jdbcMapping,
sqlAstCreationState.getCreationContext().getSessionFactory()
)
);
columnReferences.add( (ColumnReference) columnReference );
}
},
clause,
sqlAstCreationState.getCreationContext().getSessionFactory().getTypeConfiguration()
);
return new SqlTuple( columnReferences, this );
}
@Override
public String getSqlAliasStem() {
return name;
}
@Override
public ModelPart findSubPart(
String name, EntityMappingType treatTargetType) {
return mappingType.findSubPart( name, treatTargetType );
}
@Override
public void visitSubParts(
Consumer<ModelPart> consumer, EntityMappingType treatTargetType) {
mappingType.visitSubParts( consumer, treatTargetType );
}
@Override
public String getFetchableName() {
return name;
}
@Override
public FetchStrategy getMappedFetchStrategy() {
return null;
}
@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();
}
}

View File

@ -59,7 +59,7 @@ import org.hibernate.type.spi.TypeConfiguration;
* *
* @author Andrea Boriero * @author Andrea Boriero
*/ */
public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping, SingleAttributeIdentifierMapping, EmbeddableValuedFetchable { public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping, EmbeddableValuedFetchable {
private final NavigableRole navigableRole; private final NavigableRole navigableRole;
private final EntityMappingType entityMapping; private final EntityMappingType entityMapping;
private final String name; private final String name;
@ -161,12 +161,28 @@ public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping
@Override @Override
public void visitJdbcValues( public void visitJdbcValues(
Object value, Clause clause, JdbcValuesConsumer valuesConsumer, SharedSessionContractImplementor session) { Object value,
getEmbeddableTypeDescriptor().visitJdbcValues( value, clause, valuesConsumer, session ); Clause clause,
JdbcValuesConsumer valuesConsumer,
SharedSessionContractImplementor session) {
getEmbeddableTypeDescriptor().getAttributeMappings().forEach(
attributeMapping -> {
final Object o = attributeMapping.getPropertyAccess().getGetter().get( value );
if ( attributeMapping instanceof SingularAssociationAttributeMapping ) {
final EntityMappingType associatedEntityMappingType =
( (SingularAssociationAttributeMapping) attributeMapping ).getAssociatedEntityMappingType();
final EntityIdentifierMapping identifierMapping =
associatedEntityMappingType.getIdentifierMapping();
final Object identifier = identifierMapping.getIdentifier( o, session );
identifierMapping.visitJdbcValues( identifier, clause, valuesConsumer, session );
}
else {
attributeMapping.visitJdbcValues( o, clause, valuesConsumer, session );
}
}
);
} }
@Override @Override
public <T> DomainResult<T> createDomainResult( public <T> DomainResult<T> createDomainResult(
NavigablePath navigablePath, NavigablePath navigablePath,
@ -218,7 +234,6 @@ public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping
); );
return new SqlTuple( columnReferences, this ); return new SqlTuple( columnReferences, this );
} }
@Override @Override
@ -237,13 +252,10 @@ public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping
lhs lhs
); );
lhs.addTableGroupJoin( new TableGroupJoin( navigablePath, SqlAstJoinType.INNER, compositeTableGroup, null ) ); final TableGroupJoin join = new TableGroupJoin( navigablePath, SqlAstJoinType.LEFT, compositeTableGroup, null );
lhs.addTableGroupJoin( join );
return new TableGroupJoin( return join;
navigablePath,
sqlAstJoinType,
compositeTableGroup
);
} }
@Override @Override
@ -258,7 +270,8 @@ public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping
@Override @Override
public void visitSubParts( public void visitSubParts(
Consumer<ModelPart> consumer, EntityMappingType treatTargetType) { Consumer<ModelPart> consumer,
EntityMappingType treatTargetType) {
getMappedTypeDescriptor().visitSubParts( consumer, treatTargetType ); getMappedTypeDescriptor().visitSubParts( consumer, treatTargetType );
} }
@ -297,8 +310,19 @@ public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping
return getEmbeddableTypeDescriptor().getNumberOfAttributeMappings(); return getEmbeddableTypeDescriptor().getNumberOfAttributeMappings();
} }
@Override
public void visitColumns(ColumnConsumer consumer) { public void visitColumns(ColumnConsumer consumer) {
getEmbeddableTypeDescriptor().visitColumns( consumer ); getAttributes().forEach(
attribute -> {
if ( attribute instanceof SingularAssociationAttributeMapping ) {
SingularAssociationAttributeMapping associationAttributeMapping = (SingularAssociationAttributeMapping) attribute;
associationAttributeMapping.getForeignKeyDescriptor().visitReferringColumns( consumer );
}
else {
attribute.visitColumns( consumer );
}
}
);
} }
@Override @Override
@ -317,8 +341,4 @@ public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping
return (Collection) getEmbeddableTypeDescriptor().getAttributeMappings(); return (Collection) getEmbeddableTypeDescriptor().getAttributeMappings();
} }
@Override
public PropertyAccess getPropertyAccess() {
return propertyAccess;
}
} }

View File

@ -7,6 +7,7 @@
package org.hibernate.metamodel.mapping.internal; package org.hibernate.metamodel.mapping.internal;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.SortedMap; import java.util.SortedMap;
@ -49,6 +50,7 @@ import org.hibernate.metamodel.mapping.CollectionMappingType;
import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.CompositeIdentifierMapping; import org.hibernate.metamodel.mapping.CompositeIdentifierMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
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.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
@ -229,7 +231,10 @@ public class MappingModelCreationHelper {
.getDomainModel() .getDomainModel()
.getTypeConfiguration() .getTypeConfiguration()
.getBasicTypeRegistry() .getBasicTypeRegistry()
.resolve( valueConverter.getRelationalJavaDescriptor(), resolution.getRelationalSqlTypeDescriptor() ); .resolve(
valueConverter.getRelationalJavaDescriptor(),
resolution.getRelationalSqlTypeDescriptor()
);
return new BasicValuedSingularAttributeMapping( return new BasicValuedSingularAttributeMapping(
@ -474,15 +479,6 @@ public class MappingModelCreationHelper {
final CollectionMappingType<?> collectionMappingType; final CollectionMappingType<?> collectionMappingType;
final JavaTypeDescriptorRegistry jtdRegistry = creationContext.getJavaTypeDescriptorRegistry(); final JavaTypeDescriptorRegistry jtdRegistry = creationContext.getJavaTypeDescriptorRegistry();
final ForeignKeyDescriptor keyDescriptor = interpretKeyDescriptor(
bootProperty,
bootValueMapping,
collectionDescriptor,
declaringType,
dialect,
creationProcess
);
final CollectionPart elementDescriptor = interpretElement( final CollectionPart elementDescriptor = interpretElement(
bootValueMapping, bootValueMapping,
tableExpression, tableExpression,
@ -662,7 +658,6 @@ public class MappingModelCreationHelper {
entityMappingType -> contributorMetadata, entityMappingType -> contributorMetadata,
collectionMappingType, collectionMappingType,
stateArrayPosition, stateArrayPosition,
keyDescriptor,
elementDescriptor, elementDescriptor,
indexDescriptor, indexDescriptor,
identifierDescriptor, identifierDescriptor,
@ -678,6 +673,7 @@ public class MappingModelCreationHelper {
declaringType, declaringType,
collectionDescriptor collectionDescriptor
); );
creationProcess.registerInitializationCallback( creationProcess.registerInitializationCallback(
() -> { () -> {
try { try {
@ -693,11 +689,25 @@ public class MappingModelCreationHelper {
} }
); );
creationProcess.registerForeignKeyPostInitCallbacks(
() -> {
interpretKeyDescriptor(
pluralAttributeMapping,
bootValueMapping,
collectionDescriptor,
declaringType,
dialect,
creationProcess
);
return true;
}
);
return pluralAttributeMapping; return pluralAttributeMapping;
} }
private static ForeignKeyDescriptor interpretKeyDescriptor( private static void interpretKeyDescriptor(
Property bootProperty, PluralAttributeMappingImpl attributeMapping,
Collection bootValueMapping, Collection bootValueMapping,
CollectionPersister collectionDescriptor, CollectionPersister collectionDescriptor,
ManagedMappingType declaringType, ManagedMappingType declaringType,
@ -720,19 +730,53 @@ public class MappingModelCreationHelper {
assert fkTarget instanceof BasicValuedModelPart; assert fkTarget instanceof BasicValuedModelPart;
final BasicValuedModelPart simpleFkTarget = (BasicValuedModelPart) fkTarget; final BasicValuedModelPart simpleFkTarget = (BasicValuedModelPart) fkTarget;
return new SimpleForeignKeyDescriptor( attributeMapping.setForeignKeyDescriptor(
( (AssociationType) bootValueMapping.getType() ).getForeignKeyDirection(), new SimpleForeignKeyDescriptor(
getTableIdentifierExpression( bootValueMappingKey.getTable(), creationProcess ), ( (AssociationType) bootValueMapping.getType() ).getForeignKeyDirection(),
bootValueMappingKey.getColumnIterator().next().getText( dialect ), getTableIdentifierExpression( bootValueMappingKey.getTable(), creationProcess ),
simpleFkTarget.getContainingTableExpression(), bootValueMappingKey.getColumnIterator().next().getText( dialect ),
simpleFkTarget.getMappedColumnExpression(), simpleFkTarget.getContainingTableExpression(),
(JdbcMapping) keyType simpleFkTarget.getMappedColumnExpression(),
(JdbcMapping) keyType
)
); );
} }
else if ( fkTarget instanceof EmbeddableValuedModelPart ) {
final EmbeddableValuedModelPart embeddedFkTarget = (EmbeddableValuedModelPart) fkTarget;
List<JdbcMapping> jdbcMappings = new ArrayList<>();
embeddedFkTarget.visitJdbcTypes(
jdbcMapping -> {
jdbcMappings.add( jdbcMapping );
},
null,
creationProcess.getCreationContext().getTypeConfiguration()
);
List<String> keyColumnExpressions = new ArrayList<>();
bootValueMapping.getColumnIterator().forEachRemaining( column -> keyColumnExpressions.add( column.getText(
dialect ) ) );
List<String> targetColumnExpressions = new ArrayList<>();
fkTarget.visitColumns(
(table, column, mapping) ->
targetColumnExpressions.add( column ) );
EmbeddedForeignKeyDescriptor embeddedForeignKeyDescriptor = new EmbeddedForeignKeyDescriptor(
attributeMapping.getAttributeName(),
(EmbeddedIdentifierMappingImpl) fkTarget,
getStateArrayContributorMetadataAccess(attributeMapping.getPropertyAccess()),
( (AssociationType) bootValueMapping.getType() ).getForeignKeyDirection(),
getTableIdentifierExpression( bootValueMapping.getTable(), creationProcess ),
keyColumnExpressions,
embeddedFkTarget.getContainingTableExpression(),
targetColumnExpressions,
throw new NotYetImplementedFor6Exception( creationProcess
"Support for composite foreign-keys not yet implemented: " + bootValueMapping.getRole() );
); attributeMapping.setForeignKeyDescriptor( embeddedForeignKeyDescriptor );
}else {
throw new NotYetImplementedFor6Exception(
"Support for composite foreign-keys not yet implemented: " + bootValueMapping.getRole()
);
}
} }
public static void interpretKeyDescriptor( public static void interpretKeyDescriptor(
@ -781,7 +825,6 @@ public class MappingModelCreationHelper {
); );
attributeMapping.setForeignKeyDescriptor( foreignKeyDescriptor ); attributeMapping.setForeignKeyDescriptor( foreignKeyDescriptor );
} }
else { else {
SingularAssociationAttributeMapping subPart = (SingularAssociationAttributeMapping) referencedEntityDescriptor SingularAssociationAttributeMapping subPart = (SingularAssociationAttributeMapping) referencedEntityDescriptor
@ -803,13 +846,45 @@ public class MappingModelCreationHelper {
creationProcess creationProcess
); );
attributeMapping.setForeignKeyDescriptor( subPart.getForeignKeyDescriptor() ); attributeMapping.setForeignKeyDescriptor( subPart.getForeignKeyDescriptor() );
} }
else { else {
attributeMapping.setForeignKeyDescriptor( foreignKeyDescriptor ); attributeMapping.setForeignKeyDescriptor( foreignKeyDescriptor );
} }
} }
} }
else if ( fkTarget instanceof EmbeddableValuedModelPart ) {
if ( bootValueMapping.isReferenceToPrimaryKey() ) {
final EmbeddableValuedModelPart embeddedFkTarget = (EmbeddableValuedModelPart) fkTarget;
final List<String> keyColumnExpressions = new ArrayList<>();
bootValueMapping.getColumnIterator().forEachRemaining(
column ->
keyColumnExpressions.add( column.getText( dialect ) )
);
final List<String> targetColumnExpressions = new ArrayList<>();
embeddedFkTarget.getMappedColumnExpressions().forEach(
column ->
targetColumnExpressions.add( column )
);
final EmbeddedForeignKeyDescriptor embeddedForeignKeyDescriptor = new EmbeddedForeignKeyDescriptor(
attributeMapping.getAttributeName(),
(EmbeddedIdentifierMappingImpl) fkTarget,
getStateArrayContributorMetadataAccess(attributeMapping.getPropertyAccess()),
( (AssociationType) bootValueMapping.getType() ).getForeignKeyDirection(),
getTableIdentifierExpression( bootValueMapping.getTable(), creationProcess ),
keyColumnExpressions,
embeddedFkTarget.getContainingTableExpression(),
targetColumnExpressions,
creationProcess
);
attributeMapping.setForeignKeyDescriptor( embeddedForeignKeyDescriptor );
}
else{
throw new NotYetImplementedFor6Exception();
}
}
else { else {
throw new NotYetImplementedFor6Exception( throw new NotYetImplementedFor6Exception(
"Support for composite foreign-keys not yet implemented: " + "Support for composite foreign-keys not yet implemented: " +
@ -1091,7 +1166,7 @@ public class MappingModelCreationHelper {
propertyAccess propertyAccess
); );
creationProcess.registerInitializationCallback( creationProcess.registerForeignKeyPostInitCallbacks(
() -> { () -> {
final Dialect dialect = creationProcess.getCreationContext() final Dialect dialect = creationProcess.getCreationContext()
.getSessionFactory() .getSessionFactory()

View File

@ -37,6 +37,9 @@ public class MappingModelCreationProcess {
private String currentlyProcessingRole; private String currentlyProcessingRole;
private List<PostInitCallback> postInitCallbacks;
private List<PostInitCallback> foreignKeyPostInitCallbacks;
private MappingModelCreationProcess( private MappingModelCreationProcess(
Map<String,EntityPersister> entityPersisterMap, Map<String,EntityPersister> entityPersisterMap,
RuntimeModelCreationContext creationContext) { RuntimeModelCreationContext creationContext) {
@ -66,7 +69,13 @@ public class MappingModelCreationProcess {
entityPersister.prepareMappingModel( this ); entityPersister.prepareMappingModel( this );
} }
while ( postInitCallbacks != null && ! postInitCallbacks.isEmpty() ) { executePostInitCallbakcs( postInitCallbacks );
executePostInitCallbakcs( foreignKeyPostInitCallbacks );
}
private void executePostInitCallbakcs(List<PostInitCallback> postInitCallbacks) {
while ( postInitCallbacks != null && !postInitCallbacks.isEmpty() ) {
// copy to avoid CCME // copy to avoid CCME
final ArrayList<PostInitCallback> copy = new ArrayList<>( new ArrayList<>( postInitCallbacks ) ); final ArrayList<PostInitCallback> copy = new ArrayList<>( new ArrayList<>( postInitCallbacks ) );
@ -100,8 +109,6 @@ public class MappingModelCreationProcess {
} }
} }
private List<PostInitCallback> postInitCallbacks;
public void registerInitializationCallback(PostInitCallback callback) { public void registerInitializationCallback(PostInitCallback callback) {
if ( postInitCallbacks == null ) { if ( postInitCallbacks == null ) {
postInitCallbacks = new ArrayList<>(); postInitCallbacks = new ArrayList<>();
@ -109,6 +116,13 @@ public class MappingModelCreationProcess {
postInitCallbacks.add( callback ); postInitCallbacks.add( callback );
} }
public void registerForeignKeyPostInitCallbacks(PostInitCallback callback) {
if ( foreignKeyPostInitCallbacks == null ) {
foreignKeyPostInitCallbacks = new ArrayList<>();
}
foreignKeyPostInitCallbacks.add( callback );
}
@FunctionalInterface @FunctionalInterface
public interface PostInitCallback { public interface PostInitCallback {
boolean process(); boolean process();

View File

@ -25,6 +25,7 @@ import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor; import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor;
import org.hibernate.metamodel.mapping.CollectionMappingType; import org.hibernate.metamodel.mapping.CollectionMappingType;
import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.ManagedMappingType;
@ -61,18 +62,20 @@ import org.hibernate.sql.results.graph.collection.internal.CollectionDomainResul
import org.hibernate.sql.results.graph.collection.internal.DelayedCollectionFetch; import org.hibernate.sql.results.graph.collection.internal.DelayedCollectionFetch;
import org.hibernate.sql.results.graph.collection.internal.EagerCollectionFetch; import org.hibernate.sql.results.graph.collection.internal.EagerCollectionFetch;
import org.hibernate.sql.results.graph.collection.internal.SelectEagerCollectionFetch; import org.hibernate.sql.results.graph.collection.internal.SelectEagerCollectionFetch;
import org.hibernate.type.AssociationType;
import org.hibernate.type.EntityType; import org.hibernate.type.EntityType;
import org.hibernate.type.ForeignKeyDirection; import org.hibernate.type.ForeignKeyDirection;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import java.util.ArrayList;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class PluralAttributeMappingImpl extends AbstractAttributeMapping implements PluralAttributeMapping { public class PluralAttributeMappingImpl extends AbstractAttributeMapping implements PluralAttributeMapping {
private static final Logger log = Logger.getLogger( PluralAttributeMappingImpl.class ); private static final Logger log = Logger.getLogger( PluralAttributeMappingImpl.class );
public interface Aware { public interface Aware {
void injectAttributeMapping(PluralAttributeMapping attributeMapping); void injectAttributeMapping(PluralAttributeMapping attributeMapping);
} }
@ -83,7 +86,6 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
private final PropertyAccess propertyAccess; private final PropertyAccess propertyAccess;
private final StateArrayContributorMetadataAccess stateArrayContributorMetadataAccess; private final StateArrayContributorMetadataAccess stateArrayContributorMetadataAccess;
private final ForeignKeyDescriptor fkDescriptor;
private final CollectionPart elementDescriptor; private final CollectionPart elementDescriptor;
private final CollectionPart indexDescriptor; private final CollectionPart indexDescriptor;
private final CollectionIdentifierDescriptor identifierDescriptor; private final CollectionIdentifierDescriptor identifierDescriptor;
@ -98,6 +100,7 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
private final IndexMetadata indexMetadata; private final IndexMetadata indexMetadata;
private ForeignKeyDescriptor fkDescriptor;
private ForeignKeyDescriptor manyToManyFkDescriptor; private ForeignKeyDescriptor manyToManyFkDescriptor;
private OrderByFragment orderByFragment; private OrderByFragment orderByFragment;
@ -111,7 +114,6 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
StateArrayContributorMetadataAccess stateArrayContributorMetadataAccess, StateArrayContributorMetadataAccess stateArrayContributorMetadataAccess,
CollectionMappingType collectionMappingType, CollectionMappingType collectionMappingType,
int stateArrayPosition, int stateArrayPosition,
ForeignKeyDescriptor fkDescriptor,
CollectionPart elementDescriptor, CollectionPart elementDescriptor,
CollectionPart indexDescriptor, CollectionPart indexDescriptor,
CollectionIdentifierDescriptor identifierDescriptor, CollectionIdentifierDescriptor identifierDescriptor,
@ -124,7 +126,6 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
this.stateArrayContributorMetadataAccess = stateArrayContributorMetadataAccess; this.stateArrayContributorMetadataAccess = stateArrayContributorMetadataAccess;
this.collectionMappingType = collectionMappingType; this.collectionMappingType = collectionMappingType;
this.stateArrayPosition = stateArrayPosition; this.stateArrayPosition = stateArrayPosition;
this.fkDescriptor = fkDescriptor;
this.elementDescriptor = elementDescriptor; this.elementDescriptor = elementDescriptor;
this.indexDescriptor = indexDescriptor; this.indexDescriptor = indexDescriptor;
this.identifierDescriptor = identifierDescriptor; this.identifierDescriptor = identifierDescriptor;
@ -185,47 +186,84 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
MappingModelCreationProcess creationProcess) { MappingModelCreationProcess creationProcess) {
if ( collectionDescriptor.getElementType() instanceof EntityType if ( collectionDescriptor.getElementType() instanceof EntityType
|| collectionDescriptor.getIndexType() instanceof EntityType ) { || collectionDescriptor.getIndexType() instanceof EntityType ) {
creationProcess.registerForeignKeyPostInitCallbacks(
() -> {
final EntityPersister associatedEntityDescriptor;
final ModelPart fkTargetPart;
final Value fkBootDescriptorSource;
if ( collectionDescriptor.getElementType() instanceof EntityType ) {
final EntityType elementEntityType = (EntityType) collectionDescriptor.getElementType();
associatedEntityDescriptor = creationProcess.getEntityPersister( elementEntityType.getAssociatedEntityName() );
fkTargetPart = elementEntityType.isReferenceToPrimaryKey()
? associatedEntityDescriptor.getIdentifierMapping()
: associatedEntityDescriptor.findSubPart( elementEntityType.getRHSUniqueKeyPropertyName() );
fkBootDescriptorSource = bootDescriptor.getElement();
}
else {
assert collectionDescriptor.getIndexType() != null;
assert bootDescriptor instanceof IndexedCollection;
final EntityPersister associatedEntityDescriptor; final EntityType indexEntityType = (EntityType) collectionDescriptor.getIndexType();
final ModelPart fkTargetPart; associatedEntityDescriptor = creationProcess.getEntityPersister( indexEntityType.getAssociatedEntityName() );
final Value fkBootDescriptorSource; fkTargetPart = indexEntityType.isReferenceToPrimaryKey()
if ( collectionDescriptor.getElementType() instanceof EntityType ) { ? associatedEntityDescriptor.getIdentifierMapping()
final EntityType elementEntityType = (EntityType) collectionDescriptor.getElementType(); : associatedEntityDescriptor.findSubPart( indexEntityType.getRHSUniqueKeyPropertyName() );
associatedEntityDescriptor = creationProcess.getEntityPersister( elementEntityType.getAssociatedEntityName() ); fkBootDescriptorSource = ( (IndexedCollection) bootDescriptor ).getIndex();
fkTargetPart = elementEntityType.isReferenceToPrimaryKey() }
? associatedEntityDescriptor.getIdentifierMapping()
: associatedEntityDescriptor.findSubPart( elementEntityType.getRHSUniqueKeyPropertyName() );
fkBootDescriptorSource = bootDescriptor.getElement();
}
else {
assert collectionDescriptor.getIndexType() != null;
assert bootDescriptor instanceof IndexedCollection;
final EntityType indexEntityType = (EntityType) collectionDescriptor.getIndexType(); if ( fkTargetPart instanceof BasicValuedModelPart ) {
associatedEntityDescriptor = creationProcess.getEntityPersister( indexEntityType.getAssociatedEntityName() ); final BasicValuedModelPart basicFkTargetPart = (BasicValuedModelPart) fkTargetPart;
fkTargetPart = indexEntityType.isReferenceToPrimaryKey() final Joinable collectionDescriptorAsJoinable = (Joinable) collectionDescriptor;
? associatedEntityDescriptor.getIdentifierMapping() manyToManyFkDescriptor = new SimpleForeignKeyDescriptor(
: associatedEntityDescriptor.findSubPart( indexEntityType.getRHSUniqueKeyPropertyName() ); ForeignKeyDirection.TO_PARENT,
fkBootDescriptorSource = ( (IndexedCollection) bootDescriptor ).getIndex(); collectionDescriptorAsJoinable.getTableName(),
} fkBootDescriptorSource.getColumnIterator()
.next()
.getText( creationProcess.getCreationContext()
.getSessionFactory()
.getJdbcServices()
.getDialect() ),
basicFkTargetPart.getContainingTableExpression(),
basicFkTargetPart.getMappedColumnExpression(),
basicFkTargetPart.getJdbcMapping()
);
}
else if ( fkTargetPart instanceof EmbeddableValuedModelPart ) {
final Joinable collectionDescriptorAsJoinable = (Joinable) collectionDescriptor;
java.util.List<String> keyColumnExpressions = new ArrayList<>();
fkBootDescriptorSource.getColumnIterator()
.forEachRemaining( column -> keyColumnExpressions.add(
column.getText( creationProcess.getCreationContext()
.getSessionFactory()
.getJdbcServices()
.getDialect() ) ) );
java.util.List<String> targetColumnExpressions = new ArrayList<>();
fkTargetPart.visitColumns(
(table, column, mapping) ->
targetColumnExpressions.add( column ) );
manyToManyFkDescriptor = new EmbeddedForeignKeyDescriptor(
getAttributeName(),
(EmbeddedIdentifierMappingImpl) fkTargetPart,
stateArrayContributorMetadataAccess,
( (AssociationType) bootDescriptor.getType() ).getForeignKeyDirection(),
collectionDescriptorAsJoinable.getTableName(),
keyColumnExpressions,
( (EmbeddableValuedModelPart) fkTargetPart ).getContainingTableExpression(),
targetColumnExpressions,
creationProcess
);
}
else {
throw new NotYetImplementedFor6Exception(
"Support for composite foreign keys not yet implemented : " + collectionDescriptor.getRole()
);
}
return true;
}
);
if ( fkTargetPart instanceof BasicValuedModelPart ) {
final BasicValuedModelPart basicFkTargetPart = (BasicValuedModelPart) fkTargetPart;
final Joinable collectionDescriptorAsJoinable = (Joinable) collectionDescriptor;
manyToManyFkDescriptor = new SimpleForeignKeyDescriptor(
ForeignKeyDirection.TO_PARENT,
collectionDescriptorAsJoinable.getTableName(),
fkBootDescriptorSource.getColumnIterator().next().getText(),
basicFkTargetPart.getContainingTableExpression(),
basicFkTargetPart.getMappedColumnExpression(),
basicFkTargetPart.getJdbcMapping()
);
}
else {
throw new NotYetImplementedFor6Exception(
"Support for composite foreign keys not yet implemented : " + collectionDescriptor.getRole()
);
}
} }
final boolean hasOrder = bootDescriptor.getOrderBy() != null; final boolean hasOrder = bootDescriptor.getOrderBy() != null;
@ -457,6 +495,11 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
); );
} }
} }
public void setForeignKeyDescriptor(ForeignKeyDescriptor fkDescriptor) {
this.fkDescriptor = fkDescriptor;
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
private TableGroupJoin createOneToManyTableGroupJoin( private TableGroupJoin createOneToManyTableGroupJoin(
NavigablePath navigablePath, NavigablePath navigablePath,
@ -470,7 +513,7 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
final TableGroup tableGroup = createOneToManyTableGroup( final TableGroup tableGroup = createOneToManyTableGroup(
navigablePath, navigablePath,
sqlAstJoinType == SqlAstJoinType.INNER sqlAstJoinType == SqlAstJoinType.INNER
&& ! getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable(), && !getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable(),
lockMode, lockMode,
aliasBaseGenerator, aliasBaseGenerator,
sqlExpressionResolver, sqlExpressionResolver,
@ -513,11 +556,12 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() ); final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
final TableReference primaryTableReference = entityPartDescriptor.getEntityMappingType().createPrimaryTableReference( final TableReference primaryTableReference = entityPartDescriptor.getEntityMappingType()
sqlAliasBase, .createPrimaryTableReference(
sqlExpressionResolver, sqlAliasBase,
creationContext sqlExpressionResolver,
); creationContext
);
return new StandardTableGroup( return new StandardTableGroup(
navigablePath, navigablePath,
@ -525,7 +569,8 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
lockMode, lockMode,
primaryTableReference, primaryTableReference,
sqlAliasBase, sqlAliasBase,
(tableExpression) -> entityPartDescriptor.getEntityMappingType().containsTableReference( tableExpression ), (tableExpression) -> entityPartDescriptor.getEntityMappingType()
.containsTableReference( tableExpression ),
(tableExpression, tg) -> entityPartDescriptor.getEntityMappingType().createTableReferenceJoin( (tableExpression, tg) -> entityPartDescriptor.getEntityMappingType().createTableReferenceJoin(
tableExpression, tableExpression,
sqlAliasBase, sqlAliasBase,
@ -585,7 +630,7 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() ); final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
assert ! getCollectionDescriptor().isOneToMany(); assert !getCollectionDescriptor().isOneToMany();
final String collectionTableName = ( (Joinable) collectionDescriptor ).getTableName(); final String collectionTableName = ( (Joinable) collectionDescriptor ).getTableName();
final TableReference collectionTableReference = new TableReference( final TableReference collectionTableReference = new TableReference(
@ -596,7 +641,7 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
); );
final Consumer<TableGroup> tableGroupFinalizer; final Consumer<TableGroup> tableGroupFinalizer;
final BiFunction<String,TableGroup,TableReferenceJoin> tableReferenceJoinCreator; final BiFunction<String, TableGroup, TableReferenceJoin> tableReferenceJoinCreator;
final java.util.function.Predicate<String> tableReferenceJoinNameChecker; final java.util.function.Predicate<String> tableReferenceJoinNameChecker;
if ( elementDescriptor instanceof EntityCollectionPart || indexDescriptor instanceof EntityCollectionPart ) { if ( elementDescriptor instanceof EntityCollectionPart || indexDescriptor instanceof EntityCollectionPart ) {
final EntityCollectionPart entityPartDescriptor; final EntityCollectionPart entityPartDescriptor;
@ -619,13 +664,14 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
tableExpression, tableExpression,
sqlAliasBase, sqlAliasBase,
associatedPrimaryTable, associatedPrimaryTable,
canUseInnerJoin && ! getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable(), canUseInnerJoin && !getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable(),
sqlExpressionResolver, sqlExpressionResolver,
creationContext creationContext
); );
tableGroupFinalizer = tableGroup -> { tableGroupFinalizer = tableGroup -> {
final SqlAstJoinType joinType = canUseInnerJoin && ! getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable() final SqlAstJoinType joinType = canUseInnerJoin && !getAttributeMetadataAccess().resolveAttributeMetadata(
null ).isNullable()
? SqlAstJoinType.INNER ? SqlAstJoinType.INNER
: SqlAstJoinType.LEFT; : SqlAstJoinType.LEFT;
final TableReferenceJoin associationJoin = new TableReferenceJoin( final TableReferenceJoin associationJoin = new TableReferenceJoin(
@ -736,7 +782,7 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
} }
if ( elementDescriptor instanceof EntityCollectionPart ) { if ( elementDescriptor instanceof EntityCollectionPart ) {
return ( (EntityCollectionPart) elementDescriptor ).findSubPart(name); return ( (EntityCollectionPart) elementDescriptor ).findSubPart( name );
} }
return null; return null;
} }

View File

@ -85,7 +85,7 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
} }
@Override @Override
public DomainResult createCollectionFecthDomainResult( public DomainResult createCollectionFetchDomainResult(
NavigablePath collectionPath, NavigablePath collectionPath,
TableGroup tableGroup, TableGroup tableGroup,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
@ -140,14 +140,13 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
tableReference, tableReference,
keyColumnExpression keyColumnExpression
), ),
s -> { s ->
return new ColumnReference( new ColumnReference(
identificationVariable, identificationVariable,
keyColumnExpression, keyColumnExpression,
jdbcMapping, jdbcMapping,
creationState.getSqlAstCreationState().getCreationContext().getSessionFactory() creationState.getSqlAstCreationState().getCreationContext().getSessionFactory()
); )
}
), ),
jdbcMapping.getJavaTypeDescriptor(), jdbcMapping.getJavaTypeDescriptor(),
sqlAstCreationState.getCreationContext().getDomainModel().getTypeConfiguration() sqlAstCreationState.getCreationContext().getDomainModel().getTypeConfiguration()
@ -314,25 +313,11 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
} }
@Override @Override
public <T> T visitColumnMapping(FkColumnMappingFunction<T> function) { public boolean areTargetColumnNamesEqualsTo(String[] columnNames) {
return function.apply( if ( columnNames.length != 1 ) {
keyColumnContainingTable, return false;
keyColumnExpression , }
targetColumnContainingTable, return targetColumnExpression.equals( columnNames[0] );
targetColumnExpression ,
jdbcMapping
);
}
@Override
public void visitColumnMappings(FkColumnMappingConsumer consumer) {
consumer.consume(
keyColumnContainingTable,
keyColumnExpression,
targetColumnContainingTable,
targetColumnExpression,
jdbcMapping
);
} }
@Override @Override

View File

@ -8,7 +8,6 @@ package org.hibernate.metamodel.mapping.internal;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.mapping.ManyToOne; import org.hibernate.mapping.ManyToOne;
@ -181,7 +180,7 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu
} }
final FetchParent associationFetchParent = fetchParent.resolveContainingAssociationParent(); final FetchParent associationFetchParent = fetchParent.resolveContainingAssociationParent();
if(associationFetchParent == null){ if ( associationFetchParent == null ) {
return null; return null;
} }
final ModelPart referencedModePart = associationFetchParent.getReferencedModePart(); final ModelPart referencedModePart = associationFetchParent.getReferencedModePart();
@ -189,7 +188,7 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu
final Association associationParent = (Association) referencedModePart; final Association associationParent = (Association) referencedModePart;
if (foreignKeyDescriptor.equals( associationParent.getForeignKeyDescriptor() ) ) { if ( foreignKeyDescriptor.equals( associationParent.getForeignKeyDescriptor() ) ) {
// we need to determine the NavigablePath referring to the entity that the bi-dir // we need to determine the NavigablePath referring to the entity that the bi-dir
// fetch will "return" for its Assembler. so we walk "up" the FetchParent graph // fetch will "return" for its Assembler. so we walk "up" the FetchParent graph
// to find the "referenced entity" reference // to find the "referenced entity" reference
@ -217,23 +216,10 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu
if ( associationParentForeignKeyDescriptor.getTargetTableExpression() if ( associationParentForeignKeyDescriptor.getTargetTableExpression()
.equals( entityPersister.getTableName() ) ) { .equals( entityPersister.getTableName() ) ) {
final String[] identifierColumnNames = entityPersister.getIdentifierColumnNames(); final String[] identifierColumnNames = entityPersister.getIdentifierColumnNames();
return associationParentForeignKeyDescriptor.visitColumnMapping( (referringTable, referringColumns, targetTable, targetColumns, jdbcMapping) -> { if ( associationParentForeignKeyDescriptor.areTargetColumnNamesEqualsTo( identifierColumnNames ) ) {
// if ( identifierColumnNames.length == targetColumns.size() ) { return createBiDirectionalFetch( fetchablePath, fetchParent );
// for ( int i = 0; i < identifierColumnNames.length; i++ ) { }
// if ( !targetColumns.contains( identifierColumnNames[i] ) ) { return null;
// return null;
// }
// }
if ( identifierColumnNames.length > 1 ) {
throw new NotYetImplementedFor6Exception(
"Support for composite foreign -keys not yet implemented" );
}
if ( targetColumns.equals( identifierColumnNames[0] ) ) {
return createBiDirectionalFetch( fetchablePath, fetchParent );
}
// }
return null;
} );
} }
} }

View File

@ -217,6 +217,7 @@ import org.hibernate.type.Type;
import org.hibernate.type.TypeHelper; import org.hibernate.type.TypeHelper;
import org.hibernate.type.VersionType; import org.hibernate.type.VersionType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Basic functionality for persisting an entity via JDBC * Basic functionality for persisting an entity via JDBC
@ -1231,14 +1232,6 @@ public abstract class AbstractEntityPersister
return new EntityResultImpl( navigablePath, this, resultVariable, creationState ); return new EntityResultImpl( navigablePath, this, resultVariable, creationState );
} }
@Override
public void applySqlSelections(
NavigablePath navigablePath,
TableGroup tableGroup,
DomainResultCreationState creationState) {
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override @Override
public NaturalIdMapping getNaturalIdMapping() { public NaturalIdMapping getNaturalIdMapping() {
return naturalIdMapping; return naturalIdMapping;
@ -6450,7 +6443,27 @@ public abstract class AbstractEntityPersister
collectAttributeDefinitions(); collectAttributeDefinitions();
} }
@Override
public void visitJdbcTypes(
Consumer<JdbcMapping> action,
Clause clause,
TypeConfiguration typeConfiguration) {
getAttributeMappings().forEach(
attributeMapping -> attributeMapping.visitJdbcTypes( action, clause, typeConfiguration )
);
}
@Override
public void visitJdbcValues(
Object value,
Clause clause,
JdbcValuesConsumer consumer,
SharedSessionContractImplementor session) {
getAttributeMappings().forEach(
attributeMapping ->
attributeMapping.visitJdbcValues( value, clause, consumer, session )
);
}
@Override @Override
public EntityIdentifierDefinition getEntityKeyDefinition() { public EntityIdentifierDefinition getEntityKeyDefinition() {

View File

@ -19,7 +19,7 @@ public abstract class AbstractFetchParent implements FetchParent {
private final FetchableContainer fetchContainer; private final FetchableContainer fetchContainer;
private final NavigablePath navigablePath; private final NavigablePath navigablePath;
private List<Fetch> fetches; protected List<Fetch> fetches;
public AbstractFetchParent(FetchableContainer fetchContainer, NavigablePath navigablePath) { public AbstractFetchParent(FetchableContainer fetchContainer, NavigablePath navigablePath) {
this.fetchContainer = fetchContainer; this.fetchContainer = fetchContainer;

View File

@ -60,13 +60,13 @@ public class EagerCollectionFetch extends CollectionFetch implements FetchParent
final ForeignKeyDescriptor keyDescriptor = fetchedAttribute.getKeyDescriptor(); final ForeignKeyDescriptor keyDescriptor = fetchedAttribute.getKeyDescriptor();
if ( parentTableGroup != null ) { if ( parentTableGroup != null ) {
// join fetch // join fetch
keyContainerResult = keyDescriptor.createCollectionFecthDomainResult( fetchedPath, parentTableGroup, creationState ); keyContainerResult = keyDescriptor.createCollectionFetchDomainResult( fetchedPath, parentTableGroup, creationState );
keyCollectionResult = keyDescriptor.createDomainResult( fetchedPath, collectionTableGroup, creationState ); keyCollectionResult = keyDescriptor.createDomainResult( fetchedPath, collectionTableGroup, creationState );
} }
else { else {
// select fetch // select fetch
// todo (6.0) : we could potentially leverage batch fetching for performance // todo (6.0) : we could potentially leverage batch fetching for performance
keyContainerResult = keyDescriptor.createCollectionFecthDomainResult( fetchedPath, collectionTableGroup, creationState ); keyContainerResult = keyDescriptor.createCollectionFetchDomainResult( fetchedPath, collectionTableGroup, creationState );
// use null for `keyCollectionResult`... the initializer will see that as trigger to use // use null for `keyCollectionResult`... the initializer will see that as trigger to use
// the assembled container-key value as the collection-key value. // the assembled container-key value as the collection-key value.

View File

@ -0,0 +1,140 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.sql.results.graph.embeddable.internal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.internal.SingularAssociationAttributeMapping;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.AbstractFetchParent;
import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
import org.hibernate.sql.results.graph.entity.internal.EntityFetchSelectImpl;
/**
* @author Andrea Boriero
*/
public class EmbeddableForeignKeyResultImpl<T> extends AbstractFetchParent
implements EmbeddableResultGraphNode, DomainResult<T> {
private final String resultVariable;
public EmbeddableForeignKeyResultImpl(
List<SqlSelection> sqlSelections,
NavigablePath navigablePath,
EmbeddableValuedModelPart embeddableValuedModelPart,
String resultVariable,
DomainResultCreationState creationState) {
super( embeddableValuedModelPart.getEmbeddableTypeDescriptor(), navigablePath );
this.resultVariable = resultVariable;
AtomicInteger atomicInteger = new AtomicInteger( 0 );
fetches = new ArrayList<>();
embeddableValuedModelPart.visitFetchables(
fetchable -> {
generateFetches( sqlSelections, navigablePath, creationState, atomicInteger, fetchable );
},
null
);
}
private void generateFetches(
List<SqlSelection> sqlSelections,
NavigablePath navigablePath,
DomainResultCreationState creationState,
AtomicInteger atomicInteger,
Fetchable fetchable) {
if ( fetchable instanceof SingularAssociationAttributeMapping ) {
final SingularAssociationAttributeMapping singularAssociationAttributeMapping = (SingularAssociationAttributeMapping) fetchable;
EntityMappingType associatedEntityMappingType = singularAssociationAttributeMapping.getAssociatedEntityMappingType();
BasicResult domainResult = new BasicResult(
sqlSelections.get( atomicInteger.getAndIncrement() ).getValuesArrayPosition(),
null,
associatedEntityMappingType.getIdentifierMapping().getJavaTypeDescriptor()
);
Fetch fetch = new EntityFetchSelectImpl(
this,
singularAssociationAttributeMapping,
null,
false,
navigablePath.append( fetchable.getFetchableName() ),
domainResult,
creationState
);
fetches.add( fetch );
}
else {
final Fetch fetch = new BasicFetch(
sqlSelections.get( atomicInteger.getAndIncrement() ).getValuesArrayPosition(),
null,
navigablePath.append( fetchable.getFetchableName() ),
(BasicValuedModelPart) fetchable,
true,
null,
FetchTiming.IMMEDIATE,
creationState
);
fetches.add( fetch );
}
}
@Override
public String getResultVariable() {
return resultVariable;
}
@Override
public DomainResultAssembler<T> createResultAssembler(
Consumer<Initializer> initializerCollector, AssemblerCreationState creationState) {
final EmbeddableResultInitializer initializer = new EmbeddableResultInitializer(
this,
initializerCollector,
creationState
);
initializerCollector.accept( initializer );
//noinspection unchecked
return new EmbeddableAssembler( initializer );
}
@Override
public EmbeddableMappingType getReferencedMappingType() {
return (EmbeddableMappingType) getFetchContainer().getPartMappingType();
}
@Override
public Fetch findFetch(String fetchableName) {
return super.findFetch( fetchableName );
}
@Override
public EmbeddableMappingType getFetchContainer() {
return (EmbeddableMappingType) super.getFetchContainer();
}
@Override
public EmbeddableValuedModelPart getReferencedMappingContainer() {
return getFetchContainer().getEmbeddedValueMapping();
}
}

View File

@ -51,7 +51,7 @@ public class EntityFetchJoinedImpl extends AbstractNonLazyEntityFetch {
FetchParentAccess parentAccess, FetchParentAccess parentAccess,
Consumer<Initializer> collector, Consumer<Initializer> collector,
AssemblerCreationState creationState) { AssemblerCreationState creationState) {
return new EntityInitializerJoinedFetch( return new EntityJoinedFetchInitializer(
entityResult, entityResult,
getNavigablePath(), getNavigablePath(),
lockMode, lockMode,

View File

@ -57,7 +57,7 @@ public class EntityFetchSelectImpl extends AbstractNonJoinedEntityFetch {
FetchParentAccess parentAccess, FetchParentAccess parentAccess,
Consumer<Initializer> collector, Consumer<Initializer> collector,
AssemblerCreationState creationState) { AssemblerCreationState creationState) {
final EntityInitializerSelectFetch initializer = new EntityInitializerSelectFetch( final EntitySelectFetchInitializer initializer = new EntitySelectFetchInitializer(
getNavigablePath(), getNavigablePath(),
getReferencedMappingContainer().getEntityPersister(), getReferencedMappingContainer().getEntityPersister(),
result.createResultAssembler( collector, creationState ), result.createResultAssembler( collector, creationState ),

View File

@ -20,8 +20,9 @@ import org.hibernate.sql.results.graph.Initializer;
/** /**
* @author Andrea Boriero * @author Andrea Boriero
*/ */
public class EntityInitializerJoinedFetch extends AbstractEntityInitializer { public class EntityJoinedFetchInitializer extends AbstractEntityInitializer {
protected EntityInitializerJoinedFetch(
protected EntityJoinedFetchInitializer(
EntityResultGraphNode resultDescriptor, EntityResultGraphNode resultDescriptor,
NavigablePath navigablePath, NavigablePath navigablePath,
LockMode lockMode, LockMode lockMode,

View File

@ -22,7 +22,7 @@ import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
/** /**
* @author Andrea Boriero * @author Andrea Boriero
*/ */
public class EntityInitializerSelectFetch extends AbstractFetchParentAccess implements EntityInitializer { public class EntitySelectFetchInitializer extends AbstractFetchParentAccess implements EntityInitializer {
private final NavigablePath navigablePath; private final NavigablePath navigablePath;
private final EntityPersister concreteDescriptor; private final EntityPersister concreteDescriptor;
private final DomainResultAssembler identifierAssembler; private final DomainResultAssembler identifierAssembler;
@ -31,7 +31,7 @@ public class EntityInitializerSelectFetch extends AbstractFetchParentAccess impl
private Object entityInstance; private Object entityInstance;
protected EntityInitializerSelectFetch( protected EntitySelectFetchInitializer(
NavigablePath fetchedNavigable, NavigablePath fetchedNavigable,
EntityPersister concreteDescriptor, EntityPersister concreteDescriptor,
DomainResultAssembler identifierAssembler, DomainResultAssembler identifierAssembler,
@ -75,7 +75,7 @@ public class EntityInitializerSelectFetch extends AbstractFetchParentAccess impl
entityInstance = session.internalLoad( entityInstance = session.internalLoad(
entityName, entityName,
id, id,
false, true,
nullable nullable
); );

View File

@ -0,0 +1,256 @@
/*
* 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.compositefk;
import java.io.Serializable;
import java.util.Objects;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import org.hibernate.testing.jdbc.SQLStatementInspector;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
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.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
/**
* @author Andrea Boriero
*/
@DomainModel(
annotatedClasses = {
EagerManyToOneEmbeddedIdFKTest.System.class,
EagerManyToOneEmbeddedIdFKTest.SystemUser.class
}
)
@SessionFactory(statementInspectorClass = SQLStatementInspector.class)
public class EagerManyToOneEmbeddedIdFKTest {
@BeforeEach
public void setUp(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
PK userKey = new PK( 1, "Fab" );
SystemUser user = new SystemUser( userKey, "Fab" );
System system = new System( 1, "sub1" );
system.setUser( user );
session.save( user );
session.save( system );
}
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "delete from System" ).executeUpdate();
session.createQuery( "delete from SystemUser" ).executeUpdate();
}
);
}
@Test
public void testGet(SessionFactoryScope scope) {
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
statementInspector.clear();
scope.inTransaction(
session -> {
System system = session.get( System.class, 1 );
assertThat( system, is( notNullValue() ) );
SystemUser user = system.getUser();
assertThat( user, is( notNullValue() ) );
statementInspector.assertExecutedCount( 1 );
statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 );
}
);
}
@Test
public void testHql(SessionFactoryScope scope) {
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
statementInspector.clear();
scope.inTransaction(
session -> {
System system = (System) session.createQuery( "from System e where e.id = :id" )
.setParameter( "id", 1 ).uniqueResult();
statementInspector.assertExecutedCount( 2 );
assertThat( system, is( notNullValue() ) );
SystemUser user = system.getUser();
assertThat( user, is( notNullValue() ) );
}
);
}
@Test
public void testHqlJoin(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
System system = session.createQuery( "from System e join e.user where e.id = :id", System.class )
.setParameter( "id", 1 ).uniqueResult();
assertThat( system, is( notNullValue() ) );
SystemUser user = system.getUser();
assertThat( user, is( notNullValue() ) );
}
);
}
@Test
public void testHqlJoinFetch(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
System system = session.createQuery(
"from System e join fetch e.user where e.id = :id",
System.class
)
.setParameter( "id", 1 ).uniqueResult();
assertThat( system, is( notNullValue() ) );
SystemUser user = system.getUser();
assertThat( user, is( notNullValue() ) );
}
);
}
@Test
@FailureExpected(reason = "Embedded parameters has not yet been implemented ")
public void testEmbeddedIdParameter(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
PK superUserKey = new PK( 1, "Fab" );
System system = session.createQuery(
"from System e join fetch e.user u where u.id = :id",
System.class
).setParameter( "id", superUserKey ).uniqueResult();
assertThat( system, is( notNullValue() ) );
}
);
}
@Entity(name = "System")
public static class System {
@Id
private Integer id;
private String name;
@ManyToOne
SystemUser user;
public System() {
}
public System(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public SystemUser getUser() {
return user;
}
public void setUser(SystemUser user) {
this.user = user;
}
}
@Entity(name = "SystemUser")
public static class SystemUser {
@EmbeddedId
private PK pk;
private String name;
public SystemUser() {
}
public SystemUser(PK pk, String name) {
this.pk = pk;
this.name = name;
}
public PK getPk() {
return pk;
}
public void setPk(PK pk) {
this.pk = pk;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Embeddable
public static class PK implements Serializable {
private Integer subsystem;
private String username;
public PK(Integer subsystem, String username) {
this.subsystem = subsystem;
this.username = username;
}
private PK() {
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
PK pk = (PK) o;
return Objects.equals( subsystem, pk.subsystem ) &&
Objects.equals( username, pk.username );
}
@Override
public int hashCode() {
return Objects.hash( subsystem, username );
}
}
}

View File

@ -0,0 +1,389 @@
/*
* 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.compositefk;
import java.io.Serializable;
import java.util.Objects;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import org.hibernate.Hibernate;
import org.hibernate.testing.jdbc.SQLStatementInspector;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
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.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertTrue;
/**
* @author Andrea Boriero
*/
@DomainModel(
annotatedClasses = {
ManyToOneEmbeddedIdWithToOneFKTest.System.class,
ManyToOneEmbeddedIdWithToOneFKTest.SystemUser.class,
ManyToOneEmbeddedIdWithToOneFKTest.Subsystem.class
}
)
@SessionFactory(statementInspectorClass = SQLStatementInspector.class)
public class ManyToOneEmbeddedIdWithToOneFKTest {
@BeforeEach
public void setUp(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Subsystem subsystem = new Subsystem( 2, "sub1" );
PK userKey = new PK( subsystem, "Fab" );
SystemUser user = new SystemUser( userKey, "Fab" );
System system = new System( 1, "sub1" );
system.setUser( user );
session.save( subsystem );
session.save( user );
session.save( system );
}
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "delete from System" ).executeUpdate();
session.createQuery( "delete from SystemUser" ).executeUpdate();
session.createQuery( "delete from Subsystem" ).executeUpdate();
}
);
}
@Test
public void testGet(SessionFactoryScope scope) {
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
statementInspector.clear();
scope.inTransaction(
session -> {
System system = session.get( System.class, 1 );
assertThat( system, is( notNullValue() ) );
assertTrue( Hibernate.isInitialized( system.getUser() ) );
assertTrue( Hibernate.isInitialized( system.getUser().getPk().subsystem ) );
SystemUser user = system.getUser();
assertThat( user, is( notNullValue() ) );
statementInspector.assertExecutedCount( 1 );
statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 2 );
}
);
}
@Test
public void testHql(SessionFactoryScope scope) {
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
statementInspector.clear();
/*
select
s1_0.id_,
s1_0.name as name2_1_,
s1_0.user_subsystem_id ,
s1_0.user_username_
from
System s1_0
where
s1_0.id=?
select
s2_0.id,
s2_0.description_
from
Subsystem s2_0
where
s2_0.id=?
select
manytoonee0_.subsystem_id as subsyste3_2_0_,
manytoonee0_.username as username1_2_0_,
manytoonee0_.name as name2_2_0_
from
SystemUser manytoonee0_
where
manytoonee0_.subsystem_id=?
and manytoonee0_.username=?
*/
scope.inTransaction(
session -> {
System system = (System) session.createQuery( "from System e where e.id = :id" )
.setParameter( "id", 1 ).uniqueResult();
assertThat( system, is( notNullValue() ) );
statementInspector.assertExecutedCount( 3 );
statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 );
statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 );
statementInspector.assertNumberOfOccurrenceInQuery( 2, "join", 0 );
assertTrue( Hibernate.isInitialized( system.getUser() ) );
assertTrue( Hibernate.isInitialized( system.getUser().getPk().subsystem ) );
assertThat( system.getUser().getPk().subsystem.getDescription(),is("Fab"));
SystemUser user = system.getUser();
assertThat( user, is( notNullValue() ) );
statementInspector.assertExecutedCount( 3 );
}
);
}
@Test
public void testHqlJoin(SessionFactoryScope scope) {
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
statementInspector.clear();
scope.inTransaction(
session -> {
System system = session.createQuery( "from System e join e.user where e.id = :id", System.class )
.setParameter( "id", 1 ).uniqueResult();
statementInspector.assertExecutedCount( 3 );
statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 );
statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 );
statementInspector.assertNumberOfOccurrenceInQuery( 2, "join", 0 );
assertThat( system, is( notNullValue() ) );
SystemUser user = system.getUser();
assertThat( user, is( notNullValue() ) );
}
);
}
@Test
public void testHqlJoinFetch(SessionFactoryScope scope) {
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
statementInspector.clear();
scope.inTransaction(
session -> {
System system = session.createQuery(
"from System e join fetch e.user where e.id = :id",
System.class
)
.setParameter( "id", 1 ).uniqueResult();
statementInspector.assertExecutedCount( 2 );
statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 );
statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 );
assertThat( system, is( notNullValue() ) );
SystemUser user = system.getUser();
assertThat( user, is( notNullValue() ) );
}
);
}
@Test
@FailureExpected(reason = "Embedded parameters has not yet been implemented ")
public void testEmbeddedIdParameter(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Subsystem subsystem = new Subsystem( 2, "sub1" );
PK superUserKey = new PK( subsystem, "Fab" );
System system = session.createQuery(
"from System e join fetch e.user u where u.id = :id",
System.class
).setParameter( "id", superUserKey ).uniqueResult();
assertThat( system, is( notNullValue() ) );
}
);
}
@Test
public void testHql2(SessionFactoryScope scope) {
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
statementInspector.clear();
/*
select
s1_0.subsystem_id,
s1_0.username,
s1_0.name
from
SystemUser as s1_0
select
s1_0.id,
s1_0.description
from
Subsystem s1_0
where
s1_0.id=?
*/
scope.inTransaction(
session -> {
SystemUser system = (SystemUser) session.createQuery( "from SystemUser " )
.uniqueResult();
assertThat( system, is( notNullValue() ) );
statementInspector.assertExecutedCount( 2 );
statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 );
statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 );
assertTrue( Hibernate.isInitialized( system.getPk().subsystem ) );
}
);
}
@Entity(name = "System")
public static class System {
@Id
private Integer id;
private String name;
@ManyToOne
SystemUser user;
public System() {
}
public System(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public SystemUser getUser() {
return user;
}
public void setUser(SystemUser user) {
this.user = user;
}
}
@Entity(name = "SystemUser")
public static class SystemUser {
@EmbeddedId
private PK pk;
private String name;
public SystemUser() {
}
public SystemUser(PK pk, String name) {
this.pk = pk;
this.name = name;
}
public PK getPk() {
return pk;
}
public void setPk(PK pk) {
this.pk = pk;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Embeddable
public static class PK implements Serializable {
@ManyToOne
private Subsystem subsystem;
private String username;
public PK(Subsystem subsystem, String username) {
this.subsystem = subsystem;
this.username = username;
}
private PK() {
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
PK pk = (PK) o;
return Objects.equals( subsystem, pk.subsystem ) &&
Objects.equals( username, pk.username );
}
@Override
public int hashCode() {
return Objects.hash( subsystem, username );
}
}
@Entity(name = "Subsystem")
public static class Subsystem {
@Id
private Integer id;
private String description;
public Subsystem() {
}
public Subsystem(Integer id, String description) {
this.id = id;
this.description = description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
// public Integer getId() {
// return id;
// }
}
}

View File

@ -0,0 +1,255 @@
/*
* 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.compositefk;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
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.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
/**
* @author Andrea Boriero
*/
@DomainModel(
annotatedClasses = {
OneToManyEmbeddedIdFKTest.System.class,
OneToManyEmbeddedIdFKTest.SystemUser.class
}
)
@SessionFactory
public class OneToManyEmbeddedIdFKTest {
@BeforeEach
public void setUp(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
PK superUserKey = new PK( 1, "Fab" );
SystemUser superUser = new SystemUser( superUserKey, "Fab" );
PK userKey = new PK( 2, "Andrea" );
SystemUser user = new SystemUser( userKey, "Andrea" );
System system = new System( 1, "sub1" );
system.addUser( superUser );
system.addUser( user );
session.save( superUser );
session.save( user );
session.save( system );
}
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "delete from System" ).executeUpdate();
session.createQuery( "delete from SystemUser" ).executeUpdate();
}
);
}
@Test
public void testGet(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
System system = session.get( System.class, 1 );
assertThat( system, is( notNullValue() ) );
}
);
}
@Test
public void testHqlQuery(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
System system = (System) session.createQuery( "from System e where e.id = :id" )
.setParameter( "id", 1 ).uniqueResult();
assertThat( system, is( notNullValue() ) );
assertThat( system.getUsers().size(), is( 2 ) );
}
);
}
@Test
public void testHqlJoin(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
System system = session.createQuery( "from System e join e.users where e.id = :id", System.class )
.setParameter( "id", 1 ).uniqueResult();
assertThat( system, is( notNullValue() ) );
assertThat( system.getUsers().size(), is( 2 ) );
}
);
}
@Test
public void testHqlJoinFetch(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
System system = session.createQuery(
"from System e join fetch e.users where e.id = :id",
System.class
)
.setParameter( "id", 1 ).uniqueResult();
assertThat( system, is( notNullValue() ) );
assertThat( system.getUsers().size(), is( 2 ) );
}
);
}
@Test
@FailureExpected(reason = "Embedded parameters has not yet been implemented ")
public void testEmbeddedIdParameter(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
PK superUserKey = new PK( 1, "Fab" );
System system = session.createQuery(
"from System e join fetch e.users u where u.id = :id",
System.class
).setParameter( "id", superUserKey ).uniqueResult();
assertThat( system, is( notNullValue() ) );
assertThat( system.getUsers().size(), is( 2 ) );
}
);
}
@Entity(name = "System")
public static class System {
@Id
private Integer id;
private String name;
@OneToMany
List<SystemUser> users = new ArrayList<>();
public System() {
}
public System(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<SystemUser> getUsers() {
return users;
}
public void setUsers(List<SystemUser> users) {
this.users = users;
}
public void addUser(SystemUser user) {
this.users.add( user );
}
}
@Entity(name = "SystemUser")
public static class SystemUser {
@EmbeddedId
private PK pk;
private String name;
public SystemUser() {
}
public SystemUser(PK pk, String name) {
this.pk = pk;
this.name = name;
}
public PK getPk() {
return pk;
}
public void setPk(PK pk) {
this.pk = pk;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Embeddable
public static class PK implements Serializable {
private Integer subsystem;
private String username;
public PK(Integer subsystem, String username) {
this.subsystem = subsystem;
this.username = username;
}
private PK() {
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
PK pk = (PK) o;
return Objects.equals( subsystem, pk.subsystem ) &&
Objects.equals( username, pk.username );
}
@Override
public int hashCode() {
return Objects.hash( subsystem, username );
}
}
}

View File

@ -8,7 +8,6 @@ package org.hibernate.orm.test.onetoone;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.MapsId; import javax.persistence.MapsId;

View File

@ -0,0 +1,199 @@
/*
* 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>.
*/
/*
* 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.sql.exec;
import java.io.Serializable;
import java.util.Objects;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import org.hibernate.Hibernate;
import org.hibernate.testing.jdbc.SQLStatementInspector;
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;
/**
* @author Andrea Boriero
*/
@DomainModel(
annotatedClasses = {
EmbeddedWithManyToOneTest.SystemUser.class,
EmbeddedWithManyToOneTest.Subsystem.class
}
)
@SessionFactory(
generateStatistics = true,
statementInspectorClass = SQLStatementInspector.class)
public class EmbeddedWithManyToOneTest {
@BeforeEach
public void setUp(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Subsystem subsystem = new Subsystem( 2, "sub1" );
PK userKey = new PK( subsystem, "Fab" );
SystemUser user = new SystemUser( 1, userKey, "Fab" );
session.save( subsystem );
session.save( user );
}
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "delete from SystemUser" ).executeUpdate();
session.createQuery( "delete from Subsystem" ).executeUpdate();
}
);
}
@Test
public void testGet(SessionFactoryScope scope) {
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
statementInspector.clear();
scope.inTransaction(
session -> {
SystemUser systemUser = session.get( SystemUser.class, 1 );
assertTrue( Hibernate.isInitialized( systemUser.getPk() ) );
statementInspector.assertExecutedCount( 1 );
statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 );
}
);
}
@Test
public void testHqlSelect(SessionFactoryScope scope) {
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
statementInspector.clear();
scope.inTransaction(
session -> {
SystemUser systemUser = session.createQuery( "from SystemUser", SystemUser.class )
.uniqueResult();
assertTrue( Hibernate.isInitialized( systemUser.getPk() ) );
statementInspector.assertExecutedCount( 2 );
statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 );
statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 );
}
);
}
@Entity(name = "SystemUser")
public static class SystemUser {
@Id
private Integer id;
@Embedded
private PK pk;
private String name;
public SystemUser() {
}
public SystemUser(Integer id, PK pk, String name) {
this.id = id;
this.pk = pk;
this.name = name;
}
public PK getPk() {
return pk;
}
public void setPk(PK pk) {
this.pk = pk;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Embeddable
public static class PK implements Serializable {
@ManyToOne
private Subsystem subsystem;
private String username;
public PK(Subsystem subsystem, String username) {
this.subsystem = subsystem;
this.username = username;
}
private PK() {
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
PK pk = (PK) o;
return Objects.equals( subsystem, pk.subsystem ) &&
Objects.equals( username, pk.username );
}
@Override
public int hashCode() {
return Objects.hash( subsystem, username );
}
}
@Entity(name = "Subsystem")
public static class Subsystem {
@Id
private Integer id;
private String description;
public Subsystem() {
}
public Subsystem(Integer id, String description) {
this.id = id;
this.description = description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.testing.jdbc;
import java.util.LinkedList;
import org.hibernate.resource.jdbc.spi.StatementInspector;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* @author Andrea Boriero
*/
public class SQLStatementInspector implements StatementInspector {
private final LinkedList<String> sqlQueries = new LinkedList<>();
public SQLStatementInspector() {
}
@Override
public String inspect(String sql) {
sqlQueries.add( sql );
return sql;
}
public LinkedList<String> getSqlQueries() {
return sqlQueries;
}
public void clear() {
sqlQueries.clear();
}
public void assertExecuted(String expected) {
assertTrue( sqlQueries.contains( expected ) );
}
public void assertExecutedCount(int expected) {
assertEquals( expected, sqlQueries.size() );
}
public void assertNumberOfOccurrenceInQuery(int queryNumber, String toCheck, int expectedNumberOfOccurrences) {
String query = sqlQueries.get( queryNumber );
int actual = query.split( toCheck, -1 ).length - 1;
assertThat( actual, is( expectedNumberOfOccurrences ) );
}
}

View File

@ -406,5 +406,10 @@ public class SessionFactoryExtension
public MetadataImplementor getMetadataImplementor() { public MetadataImplementor getMetadataImplementor() {
return modelScope.getDomainModel(); return modelScope.getDomainModel();
} }
@Override
public StatementInspector getStatementInspector() {
return getSessionFactory().getSessionFactoryOptions().getStatementInspector();
}
} }
} }

View File

@ -12,6 +12,7 @@ import java.util.function.Function;
import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.resource.jdbc.spi.StatementInspector;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -30,4 +31,6 @@ public interface SessionFactoryScope {
<T> T fromTransaction(SessionImplementor session, Function<SessionImplementor, T> action); <T> T fromTransaction(SessionImplementor session, Function<SessionImplementor, T> action);
MetadataImplementor getMetadataImplementor(); MetadataImplementor getMetadataImplementor();
StatementInspector getStatementInspector();
} }