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 NavigablePath idPath = rootPath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME );
entityDescriptor.getIdentifierMapping().visitColumns(
(tab, col, jdbcMapping) -> {
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.Loader;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping;
@ -477,8 +478,11 @@ public class LoaderSelectBuilder {
LoaderSqlAstCreationState creationState,
List<Fetch> fetches) {
return (fetchable, isKeyFetchable) -> {
final NavigablePath fetchablePath = fetchParent.getNavigablePath().append( fetchable.getFetchableName() );
NavigablePath navigablePath = fetchParent.getNavigablePath();
if ( isKeyFetchable ) {
navigablePath = navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME );
}
final NavigablePath fetchablePath = navigablePath.append( fetchable.getFetchableName() );
final Fetch biDirectionalFetch = fetchable.resolveCircularFetch(
fetchablePath,

View File

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

View File

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

View File

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

View File

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

View File

@ -25,7 +25,7 @@ public interface ForeignKeyDescriptor extends VirtualModelPart {
ForeignKeyDirection getDirection();
DomainResult createCollectionFecthDomainResult(
DomainResult createCollectionFetchDomainResult(
NavigablePath collectionPath,
TableGroup tableGroup,
DomainResultCreationState creationState);
@ -54,6 +54,10 @@ public interface ForeignKeyDescriptor extends VirtualModelPart {
return PART_NAME;
}
String getReferringTableExpression();
String getTargetTableExpression();
/**
* Visits the FK "referring" columns
*/
@ -62,36 +66,10 @@ public interface ForeignKeyDescriptor extends VirtualModelPart {
visitReferringColumns( consumer );
}
String getReferringTableExpression();
void visitReferringColumns(ColumnConsumer consumer);
String getTargetTableExpression();
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
*/
public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping, SingleAttributeIdentifierMapping, EmbeddableValuedFetchable {
public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping, EmbeddableValuedFetchable {
private final NavigableRole navigableRole;
private final EntityMappingType entityMapping;
private final String name;
@ -161,11 +161,27 @@ public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping
@Override
public void visitJdbcValues(
Object value, Clause clause, JdbcValuesConsumer valuesConsumer, SharedSessionContractImplementor session) {
getEmbeddableTypeDescriptor().visitJdbcValues( value, clause, valuesConsumer, session );
Object value,
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
public <T> DomainResult<T> createDomainResult(
@ -218,7 +234,6 @@ public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping
);
return new SqlTuple( columnReferences, this );
}
@Override
@ -237,13 +252,10 @@ public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping
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(
navigablePath,
sqlAstJoinType,
compositeTableGroup
);
return join;
}
@Override
@ -258,7 +270,8 @@ public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping
@Override
public void visitSubParts(
Consumer<ModelPart> consumer, EntityMappingType treatTargetType) {
Consumer<ModelPart> consumer,
EntityMappingType treatTargetType) {
getMappedTypeDescriptor().visitSubParts( consumer, treatTargetType );
}
@ -297,8 +310,19 @@ public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping
return getEmbeddableTypeDescriptor().getNumberOfAttributeMappings();
}
@Override
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
@ -317,8 +341,4 @@ public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping
return (Collection) getEmbeddableTypeDescriptor().getAttributeMappings();
}
@Override
public PropertyAccess getPropertyAccess() {
return propertyAccess;
}
}

View File

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

View File

@ -37,6 +37,9 @@ public class MappingModelCreationProcess {
private String currentlyProcessingRole;
private List<PostInitCallback> postInitCallbacks;
private List<PostInitCallback> foreignKeyPostInitCallbacks;
private MappingModelCreationProcess(
Map<String,EntityPersister> entityPersisterMap,
RuntimeModelCreationContext creationContext) {
@ -66,6 +69,12 @@ public class MappingModelCreationProcess {
entityPersister.prepareMappingModel( this );
}
executePostInitCallbakcs( postInitCallbacks );
executePostInitCallbakcs( foreignKeyPostInitCallbacks );
}
private void executePostInitCallbakcs(List<PostInitCallback> postInitCallbacks) {
while ( postInitCallbacks != null && !postInitCallbacks.isEmpty() ) {
// copy to avoid CCME
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) {
if ( postInitCallbacks == null ) {
postInitCallbacks = new ArrayList<>();
@ -109,6 +116,13 @@ public class MappingModelCreationProcess {
postInitCallbacks.add( callback );
}
public void registerForeignKeyPostInitCallbacks(PostInitCallback callback) {
if ( foreignKeyPostInitCallbacks == null ) {
foreignKeyPostInitCallbacks = new ArrayList<>();
}
foreignKeyPostInitCallbacks.add( callback );
}
@FunctionalInterface
public interface PostInitCallback {
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.CollectionMappingType;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
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.EagerCollectionFetch;
import org.hibernate.sql.results.graph.collection.internal.SelectEagerCollectionFetch;
import org.hibernate.type.AssociationType;
import org.hibernate.type.EntityType;
import org.hibernate.type.ForeignKeyDirection;
import org.jboss.logging.Logger;
import java.util.ArrayList;
/**
* @author Steve Ebersole
*/
public class PluralAttributeMappingImpl extends AbstractAttributeMapping implements PluralAttributeMapping {
private static final Logger log = Logger.getLogger( PluralAttributeMappingImpl.class );
public interface Aware {
void injectAttributeMapping(PluralAttributeMapping attributeMapping);
}
@ -83,7 +86,6 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
private final PropertyAccess propertyAccess;
private final StateArrayContributorMetadataAccess stateArrayContributorMetadataAccess;
private final ForeignKeyDescriptor fkDescriptor;
private final CollectionPart elementDescriptor;
private final CollectionPart indexDescriptor;
private final CollectionIdentifierDescriptor identifierDescriptor;
@ -98,6 +100,7 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
private final IndexMetadata indexMetadata;
private ForeignKeyDescriptor fkDescriptor;
private ForeignKeyDescriptor manyToManyFkDescriptor;
private OrderByFragment orderByFragment;
@ -111,7 +114,6 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
StateArrayContributorMetadataAccess stateArrayContributorMetadataAccess,
CollectionMappingType collectionMappingType,
int stateArrayPosition,
ForeignKeyDescriptor fkDescriptor,
CollectionPart elementDescriptor,
CollectionPart indexDescriptor,
CollectionIdentifierDescriptor identifierDescriptor,
@ -124,7 +126,6 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
this.stateArrayContributorMetadataAccess = stateArrayContributorMetadataAccess;
this.collectionMappingType = collectionMappingType;
this.stateArrayPosition = stateArrayPosition;
this.fkDescriptor = fkDescriptor;
this.elementDescriptor = elementDescriptor;
this.indexDescriptor = indexDescriptor;
this.identifierDescriptor = identifierDescriptor;
@ -185,7 +186,8 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
MappingModelCreationProcess creationProcess) {
if ( collectionDescriptor.getElementType() instanceof EntityType
|| collectionDescriptor.getIndexType() instanceof EntityType ) {
creationProcess.registerForeignKeyPostInitCallbacks(
() -> {
final EntityPersister associatedEntityDescriptor;
final ModelPart fkTargetPart;
final Value fkBootDescriptorSource;
@ -215,17 +217,53 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
manyToManyFkDescriptor = new SimpleForeignKeyDescriptor(
ForeignKeyDirection.TO_PARENT,
collectionDescriptorAsJoinable.getTableName(),
fkBootDescriptorSource.getColumnIterator().next().getText(),
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;
}
);
}
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")
private TableGroupJoin createOneToManyTableGroupJoin(
NavigablePath navigablePath,
@ -513,7 +556,8 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
final TableReference primaryTableReference = entityPartDescriptor.getEntityMappingType().createPrimaryTableReference(
final TableReference primaryTableReference = entityPartDescriptor.getEntityMappingType()
.createPrimaryTableReference(
sqlAliasBase,
sqlExpressionResolver,
creationContext
@ -525,7 +569,8 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
lockMode,
primaryTableReference,
sqlAliasBase,
(tableExpression) -> entityPartDescriptor.getEntityMappingType().containsTableReference( tableExpression ),
(tableExpression) -> entityPartDescriptor.getEntityMappingType()
.containsTableReference( tableExpression ),
(tableExpression, tg) -> entityPartDescriptor.getEntityMappingType().createTableReferenceJoin(
tableExpression,
sqlAliasBase,
@ -625,7 +670,8 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
);
tableGroupFinalizer = tableGroup -> {
final SqlAstJoinType joinType = canUseInnerJoin && ! getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable()
final SqlAstJoinType joinType = canUseInnerJoin && !getAttributeMetadataAccess().resolveAttributeMetadata(
null ).isNullable()
? SqlAstJoinType.INNER
: SqlAstJoinType.LEFT;
final TableReferenceJoin associationJoin = new TableReferenceJoin(

View File

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

View File

@ -8,7 +8,6 @@ package org.hibernate.metamodel.mapping.internal;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming;
import org.hibernate.mapping.ManyToOne;
@ -217,23 +216,10 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu
if ( associationParentForeignKeyDescriptor.getTargetTableExpression()
.equals( entityPersister.getTableName() ) ) {
final String[] identifierColumnNames = entityPersister.getIdentifierColumnNames();
return associationParentForeignKeyDescriptor.visitColumnMapping( (referringTable, referringColumns, targetTable, targetColumns, jdbcMapping) -> {
// if ( identifierColumnNames.length == targetColumns.size() ) {
// for ( int i = 0; i < identifierColumnNames.length; i++ ) {
// if ( !targetColumns.contains( identifierColumnNames[i] ) ) {
// return null;
// }
// }
if ( identifierColumnNames.length > 1 ) {
throw new NotYetImplementedFor6Exception(
"Support for composite foreign -keys not yet implemented" );
}
if ( targetColumns.equals( identifierColumnNames[0] ) ) {
if ( associationParentForeignKeyDescriptor.areTargetColumnNamesEqualsTo( identifierColumnNames ) ) {
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.VersionType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Basic functionality for persisting an entity via JDBC
@ -1231,14 +1232,6 @@ public abstract class AbstractEntityPersister
return new EntityResultImpl( navigablePath, this, resultVariable, creationState );
}
@Override
public void applySqlSelections(
NavigablePath navigablePath,
TableGroup tableGroup,
DomainResultCreationState creationState) {
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override
public NaturalIdMapping getNaturalIdMapping() {
return naturalIdMapping;
@ -6450,7 +6443,27 @@ public abstract class AbstractEntityPersister
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
public EntityIdentifierDefinition getEntityKeyDefinition() {

View File

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

View File

@ -60,13 +60,13 @@ public class EagerCollectionFetch extends CollectionFetch implements FetchParent
final ForeignKeyDescriptor keyDescriptor = fetchedAttribute.getKeyDescriptor();
if ( parentTableGroup != null ) {
// join fetch
keyContainerResult = keyDescriptor.createCollectionFecthDomainResult( fetchedPath, parentTableGroup, creationState );
keyContainerResult = keyDescriptor.createCollectionFetchDomainResult( fetchedPath, parentTableGroup, creationState );
keyCollectionResult = keyDescriptor.createDomainResult( fetchedPath, collectionTableGroup, creationState );
}
else {
// select fetch
// 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
// 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,
Consumer<Initializer> collector,
AssemblerCreationState creationState) {
return new EntityInitializerJoinedFetch(
return new EntityJoinedFetchInitializer(
entityResult,
getNavigablePath(),
lockMode,

View File

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

View File

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

View File

@ -22,7 +22,7 @@ import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
/**
* @author Andrea Boriero
*/
public class EntityInitializerSelectFetch extends AbstractFetchParentAccess implements EntityInitializer {
public class EntitySelectFetchInitializer extends AbstractFetchParentAccess implements EntityInitializer {
private final NavigablePath navigablePath;
private final EntityPersister concreteDescriptor;
private final DomainResultAssembler identifierAssembler;
@ -31,7 +31,7 @@ public class EntityInitializerSelectFetch extends AbstractFetchParentAccess impl
private Object entityInstance;
protected EntityInitializerSelectFetch(
protected EntitySelectFetchInitializer(
NavigablePath fetchedNavigable,
EntityPersister concreteDescriptor,
DomainResultAssembler identifierAssembler,
@ -75,7 +75,7 @@ public class EntityInitializerSelectFetch extends AbstractFetchParentAccess impl
entityInstance = session.internalLoad(
entityName,
id,
false,
true,
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.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
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() {
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.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.resource.jdbc.spi.StatementInspector;
/**
* @author Steve Ebersole
@ -30,4 +31,6 @@ public interface SessionFactoryScope {
<T> T fromTransaction(SessionImplementor session, Function<SessionImplementor, T> action);
MetadataImplementor getMetadataImplementor();
StatementInspector getStatementInspector();
}