HHH-13725 - Implement ToOne Associations support
This commit is contained in:
parent
83a1eb5715
commit
403bf9257c
|
@ -9,5 +9,6 @@ package org.hibernate.metamodel.mapping;
|
|||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface SingularAttributeMapping extends AttributeMapping, StateArrayContributorMapping {
|
||||
public interface SingularAttributeMapping
|
||||
extends AttributeMapping, StateArrayContributorMapping {
|
||||
}
|
||||
|
|
|
@ -85,6 +85,7 @@ import org.hibernate.sql.results.spi.Fetch;
|
|||
import org.hibernate.sql.results.spi.FetchParent;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.CompositeType;
|
||||
import org.hibernate.type.EntityType;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
|
@ -394,7 +395,6 @@ public class MappingModelCreationHelper {
|
|||
}
|
||||
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Non-identifier attributes
|
||||
|
||||
|
@ -508,7 +508,6 @@ public class MappingModelCreationHelper {
|
|||
}
|
||||
|
||||
|
||||
|
||||
public static EmbeddedAttributeMapping buildEmbeddedAttributeMapping(
|
||||
String attrName,
|
||||
int stateArrayPosition,
|
||||
|
@ -520,7 +519,41 @@ public class MappingModelCreationHelper {
|
|||
PropertyAccess propertyAccess,
|
||||
CascadeStyle cascadeStyle,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
final StateArrayContributorMetadataAccess attributeMetadataAccess = entityMappingType -> new StateArrayContributorMetadata() {
|
||||
final StateArrayContributorMetadataAccess attributeMetadataAccess = getStateArrayContributorMetadataAccess(
|
||||
bootProperty,
|
||||
attrType,
|
||||
propertyAccess,
|
||||
cascadeStyle,
|
||||
creationProcess
|
||||
);
|
||||
|
||||
final EmbeddableMappingType embeddableMappingType = EmbeddableMappingType.from(
|
||||
(Component) bootProperty.getValue(),
|
||||
attrType,
|
||||
attributeMappingType -> new EmbeddedAttributeMapping(
|
||||
attrName,
|
||||
stateArrayPosition,
|
||||
tableExpression,
|
||||
attrColumnNames,
|
||||
attributeMetadataAccess,
|
||||
FetchStrategy.IMMEDIATE_JOIN,
|
||||
attributeMappingType,
|
||||
declaringType,
|
||||
propertyAccess
|
||||
),
|
||||
creationProcess
|
||||
);
|
||||
|
||||
return (EmbeddedAttributeMapping) embeddableMappingType.getEmbeddedValueMapping();
|
||||
}
|
||||
|
||||
protected static StateArrayContributorMetadataAccess getStateArrayContributorMetadataAccess(
|
||||
Property bootProperty,
|
||||
Type attrType,
|
||||
PropertyAccess propertyAccess,
|
||||
CascadeStyle cascadeStyle,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
return entityMappingType -> new StateArrayContributorMetadata() {
|
||||
private final boolean nullable = bootProperty.getValue().isNullable();
|
||||
private final boolean insertable = bootProperty.isInsertable();
|
||||
private final boolean updateable = bootProperty.isUpdateable();
|
||||
|
@ -602,25 +635,6 @@ public class MappingModelCreationHelper {
|
|||
return cascadeStyle;
|
||||
}
|
||||
};
|
||||
|
||||
final EmbeddableMappingType embeddableMappingType = EmbeddableMappingType.from(
|
||||
(Component) bootProperty.getValue(),
|
||||
attrType,
|
||||
attributeMappingType -> new EmbeddedAttributeMapping(
|
||||
attrName,
|
||||
stateArrayPosition,
|
||||
tableExpression,
|
||||
attrColumnNames,
|
||||
attributeMetadataAccess,
|
||||
FetchStrategy.IMMEDIATE_JOIN,
|
||||
attributeMappingType,
|
||||
declaringType,
|
||||
propertyAccess
|
||||
),
|
||||
creationProcess
|
||||
);
|
||||
|
||||
return (EmbeddedAttributeMapping) embeddableMappingType.getEmbeddedValueMapping();
|
||||
}
|
||||
|
||||
public static PluralAttributeMapping buildPluralAttributeMapping(
|
||||
|
@ -780,7 +794,7 @@ public class MappingModelCreationHelper {
|
|||
}
|
||||
default: {
|
||||
throw new MappingException(
|
||||
"Unexpected CollectionClassification : " + collectionSemantics.getCollectionClassification()
|
||||
"Unexpected CollectionClassification : " + collectionSemantics.getCollectionClassification()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1084,4 +1098,51 @@ public class MappingModelCreationHelper {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static SingularAssociationAttributeMapping buildSingularAssociationAttributeMapping(
|
||||
String attrName,
|
||||
int stateArrayPosition,
|
||||
Property bootProperty,
|
||||
ManagedMappingType declaringType,
|
||||
EntityType attrType,
|
||||
PropertyAccess propertyAccess,
|
||||
CascadeStyle cascadeStyle,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
ToOne value = (ToOne) bootProperty.getValue();
|
||||
final EntityPersister entityPersister = creationProcess.getEntityPersister(
|
||||
value.getReferencedEntityName() );
|
||||
|
||||
final StateArrayContributorMetadataAccess stateArrayContributorMetadataAccess = getStateArrayContributorMetadataAccess(
|
||||
bootProperty,
|
||||
attrType,
|
||||
propertyAccess,
|
||||
cascadeStyle,
|
||||
creationProcess
|
||||
);
|
||||
final Dialect dialect = creationProcess.getCreationContext()
|
||||
.getSessionFactory()
|
||||
.getJdbcServices()
|
||||
.getJdbcEnvironment()
|
||||
.getDialect();
|
||||
|
||||
final ForeignKeyDescriptor foreignKeyDescriptor = interpretKeyDescriptor(
|
||||
bootProperty,
|
||||
value,
|
||||
entityPersister,
|
||||
dialect,
|
||||
creationProcess
|
||||
);
|
||||
// todo (6.0) : determine the correct FetchStrategy
|
||||
return new SingularAssociationAttributeMapping(
|
||||
attrName,
|
||||
stateArrayPosition,
|
||||
foreignKeyDescriptor,
|
||||
stateArrayContributorMetadataAccess,
|
||||
FetchStrategy.IMMEDIATE_JOIN,
|
||||
entityPersister,
|
||||
declaringType,
|
||||
propertyAccess
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,16 +6,36 @@
|
|||
*/
|
||||
package org.hibernate.metamodel.mapping.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.FetchStrategy;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||
import org.hibernate.property.access.spi.PropertyAccess;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.JoinType;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasStemHelper;
|
||||
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.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupBuilder;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
|
||||
import org.hibernate.sql.ast.tree.from.TableReferenceCollector;
|
||||
import org.hibernate.sql.results.internal.domain.entity.DelayedEntityFetch;
|
||||
import org.hibernate.sql.results.internal.domain.entity.EntityFetchImpl;
|
||||
import org.hibernate.sql.results.spi.DomainResult;
|
||||
import org.hibernate.sql.results.spi.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.spi.Fetch;
|
||||
import org.hibernate.sql.results.spi.FetchParent;
|
||||
|
@ -23,10 +43,15 @@ import org.hibernate.sql.results.spi.FetchParent;
|
|||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SingularAssociationAttributeMapping extends AbstractSingularAttributeMapping implements EntityValuedModelPart {
|
||||
public class SingularAssociationAttributeMapping extends AbstractSingularAttributeMapping
|
||||
implements EntityValuedModelPart, TableGroupJoinProducer {
|
||||
private final String sqlAliasStem;
|
||||
private final ForeignKeyDescriptor foreignKeyDescriptor;
|
||||
|
||||
public SingularAssociationAttributeMapping(
|
||||
String name,
|
||||
int stateArrayPosition,
|
||||
ForeignKeyDescriptor foreignKeyDescriptor,
|
||||
StateArrayContributorMetadataAccess attributeMetadataAccess,
|
||||
FetchStrategy mappedFetchStrategy,
|
||||
EntityMappingType type,
|
||||
|
@ -41,6 +66,9 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu
|
|||
declaringType,
|
||||
propertyAccess
|
||||
);
|
||||
this.sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( name );
|
||||
|
||||
this.foreignKeyDescriptor = foreignKeyDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -50,7 +78,7 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu
|
|||
|
||||
@Override
|
||||
public EntityMappingType getEntityMappingType() {
|
||||
return getMappedTypeDescriptor();
|
||||
return (EntityMappingType)getMappedTypeDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -62,11 +90,120 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu
|
|||
LockMode lockMode,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
if ( fetchTiming == FetchTiming.IMMEDIATE && selected ) {
|
||||
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
|
||||
|
||||
TableGroup lhsTableGroup = sqlAstCreationState.getFromClauseAccess()
|
||||
.getTableGroup( fetchParent.getNavigablePath() );
|
||||
|
||||
// todo (6.0) : determine the correct JoinType
|
||||
final TableGroupJoin tableGroupJoin = createTableGroupJoin(
|
||||
fetchablePath,
|
||||
lhsTableGroup,
|
||||
null,
|
||||
JoinType.INNER,
|
||||
lockMode,
|
||||
creationState.getSqlAliasBaseManager(),
|
||||
creationState.getSqlAstCreationState().getSqlExpressionResolver(),
|
||||
creationState.getSqlAstCreationState().getCreationContext()
|
||||
);
|
||||
|
||||
lhsTableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
|
||||
sqlAstCreationState.getFromClauseAccess().registerTableGroup(
|
||||
fetchablePath,
|
||||
tableGroupJoin.getJoinedGroup()
|
||||
);
|
||||
|
||||
return new EntityFetchImpl(
|
||||
fetchParent,
|
||||
this,
|
||||
lockMode,
|
||||
!selected,
|
||||
fetchablePath,
|
||||
foreignKeyDescriptor.createDomainResult( fetchablePath, lhsTableGroup, creationState ),
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
return new DelayedEntityFetch(
|
||||
fetchParent,
|
||||
this,
|
||||
lockMode,
|
||||
!selected,
|
||||
fetchablePath,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumberOfFetchables() {
|
||||
return getEntityMappingType().getNumberOfFetchables();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableGroupJoin createTableGroupJoin(
|
||||
NavigablePath navigablePath,
|
||||
TableGroup lhs,
|
||||
String explicitSourceAlias,
|
||||
JoinType joinType,
|
||||
LockMode lockMode,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final String aliasRoot = explicitSourceAlias == null ? sqlAliasStem : explicitSourceAlias;
|
||||
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( aliasRoot );
|
||||
|
||||
final TableGroupBuilder tableGroupBuilder = TableGroupBuilder.builder(
|
||||
navigablePath,
|
||||
this,
|
||||
lockMode,
|
||||
sqlAliasBase,
|
||||
creationContext.getSessionFactory()
|
||||
);
|
||||
|
||||
applyTableReferences(
|
||||
sqlAliasBase,
|
||||
joinType,
|
||||
tableGroupBuilder,
|
||||
sqlExpressionResolver,
|
||||
creationContext
|
||||
);
|
||||
|
||||
getMappedTypeDescriptor().getIdentifierMapping();
|
||||
|
||||
final TableGroup tableGroup = tableGroupBuilder.build();
|
||||
final TableGroupJoin tableGroupJoin = new TableGroupJoin(
|
||||
navigablePath,
|
||||
joinType,
|
||||
tableGroup,
|
||||
null
|
||||
);
|
||||
|
||||
lhs.addTableGroupJoin( tableGroupJoin );
|
||||
|
||||
return tableGroupJoin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSqlAliasStem() {
|
||||
return sqlAliasStem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyTableReferences(
|
||||
SqlAliasBase sqlAliasBase,
|
||||
JoinType baseJoinType,
|
||||
TableReferenceCollector collector,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
SqlAstCreationContext creationContext) {
|
||||
getMappedTypeDescriptor().applyTableReferences(
|
||||
sqlAliasBase,
|
||||
baseJoinType,
|
||||
collector,
|
||||
sqlExpressionResolver,
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -138,6 +138,7 @@ import org.hibernate.metadata.ClassMetadata;
|
|||
import org.hibernate.metamodel.RepresentationMode;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.AttributeMetadata;
|
||||
import org.hibernate.metamodel.mapping.AttributeMetadataAccess;
|
||||
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
|
@ -5367,7 +5368,9 @@ public abstract class AbstractEntityPersister
|
|||
else {
|
||||
final Object[] values = new Object[ getNumberOfAttributeMappings() ];
|
||||
for ( int i = 0; i < attributeMappings.size(); i++ ) {
|
||||
values[ i ] = attributeMappings.get( i ).getAttributeMetadataAccess()
|
||||
AttributeMapping attributeMapping = attributeMappings.get( i );
|
||||
AttributeMetadataAccess attributeMetadataAccess = attributeMapping.getAttributeMetadataAccess();
|
||||
values[ i ] = attributeMetadataAccess
|
||||
.resolveAttributeMetadata( this )
|
||||
.getPropertyAccess()
|
||||
.getGetter()
|
||||
|
@ -6433,6 +6436,18 @@ public abstract class AbstractEntityPersister
|
|||
creationProcess
|
||||
);
|
||||
}
|
||||
else if ( attrType instanceof EntityType ) {
|
||||
return MappingModelCreationHelper.buildSingularAssociationAttributeMapping(
|
||||
attrName,
|
||||
stateArrayPosition,
|
||||
bootProperty,
|
||||
this,
|
||||
(EntityType) attrType,
|
||||
propertyAccess,
|
||||
tupleAttrDefinition.getCascadeStyle(),
|
||||
creationProcess
|
||||
);
|
||||
}
|
||||
|
||||
// todo (6.0) : for now ignore any non basic-typed attributes
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.hibernate.query.sqm.spi.BaseSemanticQueryWalker;
|
|||
import org.hibernate.query.sqm.spi.JdbcParameterBySqmParameterAccess;
|
||||
import org.hibernate.query.sqm.sql.internal.BasicValuedPathInterpretation;
|
||||
import org.hibernate.query.sqm.sql.internal.EmbeddableValuedPathInterpretation;
|
||||
import org.hibernate.query.sqm.sql.internal.EntityValuedPathInterpretation;
|
||||
import org.hibernate.query.sqm.sql.internal.SqlAstQuerySpecProcessingStateImpl;
|
||||
import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation;
|
||||
import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation;
|
||||
|
@ -743,10 +744,7 @@ public abstract class BaseSqmToSqlAstConverter
|
|||
|
||||
@Override
|
||||
public SqmPathInterpretation<?> visitEntityValuedPath(SqmEntityValuedSimplePath sqmPath) {
|
||||
final SqmPath<?> lhs = sqmPath.getLhs();
|
||||
assert lhs != null;
|
||||
|
||||
return (SqmPathInterpretation) sqmPath;
|
||||
return EntityValuedPathInterpretation.from( sqmPath, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.query.sqm.sql.internal;
|
||||
|
||||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.spi.SqlAstWalker;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.results.spi.DomainResult;
|
||||
import org.hibernate.sql.results.spi.DomainResultCreationState;
|
||||
|
||||
/**
|
||||
* @author Koen Aers
|
||||
*/
|
||||
public class EntityValuedPathInterpretation<T> implements SqmPathInterpretation<T> {
|
||||
|
||||
public static <T> EntityValuedPathInterpretation<T> from(
|
||||
SqmEntityValuedSimplePath<T> sqmPath,
|
||||
SqlAstCreationState sqlAstCreationState) {
|
||||
final TableGroup tableGroup = sqlAstCreationState
|
||||
.getFromClauseAccess()
|
||||
.findTableGroup( sqmPath.getLhs().getNavigablePath() );
|
||||
final EntityValuedModelPart mapping = (EntityValuedModelPart) tableGroup
|
||||
.getModelPart()
|
||||
.findSubPart( sqmPath.getReferencedPathSource().getPathName(),null );
|
||||
return new EntityValuedPathInterpretation<>(
|
||||
sqmPath,
|
||||
tableGroup,
|
||||
mapping);
|
||||
}
|
||||
|
||||
private EntityValuedModelPart mapping = null;
|
||||
private TableGroup tableGroup = null;
|
||||
private SqmEntityValuedSimplePath sqmPath = null;
|
||||
|
||||
private EntityValuedPathInterpretation(
|
||||
SqmEntityValuedSimplePath sqmPath,
|
||||
TableGroup tableGroup,
|
||||
EntityValuedModelPart mapping) {
|
||||
this.tableGroup = tableGroup;
|
||||
this.mapping = mapping;
|
||||
this.sqmPath = sqmPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResult<T> createDomainResult(
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
return mapping.createDomainResult(
|
||||
getNavigablePath(),
|
||||
tableGroup,
|
||||
resultVariable,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmPath<T> getInterpretedSqmPath() {
|
||||
return sqmPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelPart getExpressionType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(SqlAstWalker sqlTreeWalker) {
|
||||
|
||||
}
|
||||
}
|
|
@ -115,7 +115,7 @@ public class DelayedCollectionAssembler implements DomainResultAssembler {
|
|||
persistenceContext.addUninitializedCollection(
|
||||
getInitializingCollectionDescriptor(),
|
||||
instance,
|
||||
(Serializable) collectionKey.getKey()
|
||||
collectionKey.getKey()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.sql.results.internal.domain.entity;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class DelayedEntityAssembler {
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.sql.results.internal.domain.entity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.spi.CollectionKey;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.metamodel.mapping.internal.SingularAssociationAttributeMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.results.internal.domain.AbstractFetchParentAccess;
|
||||
import org.hibernate.sql.results.spi.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.spi.DomainResult;
|
||||
import org.hibernate.sql.results.spi.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.spi.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.spi.EntityInitializer;
|
||||
import org.hibernate.sql.results.spi.Fetch;
|
||||
import org.hibernate.sql.results.spi.FetchParent;
|
||||
import org.hibernate.sql.results.spi.FetchParentAccess;
|
||||
import org.hibernate.sql.results.spi.Fetchable;
|
||||
import org.hibernate.sql.results.spi.Initializer;
|
||||
import org.hibernate.sql.results.spi.RowProcessingState;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class DelayedEntityFetch implements Fetch {
|
||||
|
||||
private FetchParent fetchParent;
|
||||
private SingularAssociationAttributeMapping fetchedAttribute;
|
||||
private final LockMode lockMode;
|
||||
private final NavigablePath navigablePath;
|
||||
private final boolean nullable;
|
||||
private final DomainResultCreationState creationState;
|
||||
|
||||
public DelayedEntityFetch(
|
||||
FetchParent fetchParent,
|
||||
SingularAssociationAttributeMapping fetchedAttribute,
|
||||
LockMode lockMode,
|
||||
boolean nullable,
|
||||
NavigablePath navigablePath,
|
||||
DomainResultCreationState creationState) {
|
||||
this.fetchParent = fetchParent;
|
||||
this.fetchedAttribute = fetchedAttribute;
|
||||
this.lockMode = lockMode;
|
||||
this.nullable = nullable;
|
||||
this.navigablePath = navigablePath;
|
||||
this.creationState = creationState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchParent getFetchParent() {
|
||||
return fetchParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetchable getFetchedMapping() {
|
||||
return fetchedAttribute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getNavigablePath() {
|
||||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNullable() {
|
||||
return nullable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler createAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
Consumer<Initializer> collector,
|
||||
AssemblerCreationState creationState) {
|
||||
EntityInitializer entityInitializer = new DelayedEntityFectInitializer(
|
||||
parentAccess,
|
||||
navigablePath,
|
||||
(EntityPersister) fetchedAttribute.getMappedTypeDescriptor()
|
||||
);
|
||||
collector.accept( entityInitializer );
|
||||
return new EntityAssembler( fetchedAttribute.getJavaTypeDescriptor(), entityInitializer );
|
||||
|
||||
}
|
||||
|
||||
private static class DelayedEntityFectInitializer extends AbstractFetchParentAccess implements EntityInitializer {
|
||||
|
||||
private final FetchParentAccess parentAccess;
|
||||
private final NavigablePath navigablePath;
|
||||
private final EntityPersister concreteDescriptor;
|
||||
|
||||
private Object entityInstance;
|
||||
|
||||
protected DelayedEntityFectInitializer(
|
||||
FetchParentAccess parentAccess,
|
||||
NavigablePath fetchedNavigable,
|
||||
EntityPersister concreteDescriptor
|
||||
) {
|
||||
this.parentAccess = parentAccess;
|
||||
this.navigablePath = fetchedNavigable;
|
||||
this.concreteDescriptor = concreteDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getNavigablePath() {
|
||||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveKey(RowProcessingState rowProcessingState) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
final EntityKey entityKey = new EntityKey(
|
||||
parentAccess.getParentKey(),
|
||||
concreteDescriptor
|
||||
);
|
||||
Object fkValue = entityKey.getIdentifierValue();
|
||||
|
||||
// todo (6.0) : technically the entity could be managed or cached already. who/what handles that?
|
||||
|
||||
// todo (6.0) : could also be getting loaded elsewhere (LoadingEntityEntry)
|
||||
if ( fkValue == null ) {
|
||||
// todo (6.0) : check this is the correct behaviour
|
||||
entityInstance = null;
|
||||
}
|
||||
else {
|
||||
if ( concreteDescriptor.hasProxy() ) {
|
||||
entityInstance = concreteDescriptor.createProxy(
|
||||
fkValue,
|
||||
rowProcessingState.getSession()
|
||||
);
|
||||
}
|
||||
else if ( concreteDescriptor
|
||||
.getBytecodeEnhancementMetadata()
|
||||
.isEnhancedForLazyLoading() ) {
|
||||
entityInstance = concreteDescriptor.instantiate(
|
||||
fkValue,
|
||||
rowProcessingState.getSession()
|
||||
);
|
||||
}
|
||||
|
||||
notifyParentResolutionListeners( entityInstance );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance(RowProcessingState rowProcessingState) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||
entityInstance = null;
|
||||
|
||||
clearParentResolutionListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityPersister getEntityDescriptor() {
|
||||
return concreteDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEntityInstance() {
|
||||
return entityInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getParentKey() {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerResolutionListener(Consumer<Object> listener) {
|
||||
if ( entityInstance != null ) {
|
||||
listener.accept( entityInstance );
|
||||
}
|
||||
else {
|
||||
super.registerResolutionListener( listener );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.sql.results.internal.domain.entity;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.internal.SingularAssociationAttributeMapping;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.results.spi.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.spi.DomainResult;
|
||||
import org.hibernate.sql.results.spi.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.spi.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.spi.EntityInitializer;
|
||||
import org.hibernate.sql.results.spi.Fetch;
|
||||
import org.hibernate.sql.results.spi.FetchParent;
|
||||
import org.hibernate.sql.results.spi.FetchParentAccess;
|
||||
import org.hibernate.sql.results.spi.Fetchable;
|
||||
import org.hibernate.sql.results.spi.Initializer;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class EntityFetchImpl implements Fetch {
|
||||
|
||||
private final FetchParent fetchParent;
|
||||
private final SingularAssociationAttributeMapping fetchedAttribute;
|
||||
private final LockMode lockMode;
|
||||
private boolean nullable;
|
||||
private final NavigablePath navigablePath;
|
||||
private final DomainResult fkResult;
|
||||
private final EntityResultImpl entityResult;
|
||||
|
||||
public EntityFetchImpl(
|
||||
FetchParent fetchParent,
|
||||
SingularAssociationAttributeMapping fetchedAttribute,
|
||||
LockMode lockMode,
|
||||
boolean nullable,
|
||||
NavigablePath navigablePath,
|
||||
DomainResult fkResult,
|
||||
DomainResultCreationState creationState) {
|
||||
this.fetchParent = fetchParent;
|
||||
this.fetchedAttribute = fetchedAttribute;
|
||||
this.lockMode = lockMode;
|
||||
this.nullable = nullable;
|
||||
this.navigablePath = navigablePath;
|
||||
this.fkResult = fkResult;
|
||||
|
||||
entityResult = new EntityResultImpl( navigablePath,
|
||||
(EntityValuedModelPart) fetchedAttribute.getMappedTypeDescriptor(), null, creationState );
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchParent getFetchParent() {
|
||||
return fetchParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetchable getFetchedMapping() {
|
||||
return fetchedAttribute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getNavigablePath() {
|
||||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNullable() {
|
||||
return nullable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler createAssembler(
|
||||
FetchParentAccess parentAccess, Consumer<Initializer> collector, AssemblerCreationState creationState) {
|
||||
EntityInitializer entityInitializer = new EntityFetchInitializer(
|
||||
entityResult,
|
||||
navigablePath,
|
||||
lockMode,
|
||||
entityResult.getIdentifierResult(),
|
||||
entityResult.getDiscriminatorResult(),
|
||||
entityResult.getVersionResult(),
|
||||
collector,
|
||||
creationState
|
||||
);
|
||||
return new EntityAssembler( fetchedAttribute.getJavaTypeDescriptor(), entityInitializer );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.sql.results.internal.domain.entity;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.internal.log.LoggingHelper;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.results.spi.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.spi.DomainResult;
|
||||
import org.hibernate.sql.results.spi.EntityResultNode;
|
||||
import org.hibernate.sql.results.spi.Initializer;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class EntityFetchInitializer extends AbstractEntityInitializer {
|
||||
protected EntityFetchInitializer(
|
||||
EntityResultNode resultDescriptor,
|
||||
NavigablePath navigablePath,
|
||||
LockMode lockMode,
|
||||
DomainResult<?> identifierResult,
|
||||
DomainResult<?> discriminatorResult,
|
||||
DomainResult<?> versionResult,
|
||||
Consumer<Initializer> initializerConsumer,
|
||||
AssemblerCreationState creationState) {
|
||||
super(
|
||||
resultDescriptor,
|
||||
navigablePath,
|
||||
lockMode,
|
||||
identifierResult,
|
||||
discriminatorResult,
|
||||
versionResult,
|
||||
initializerConsumer,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEntityReturn() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EntityFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
|
||||
}
|
||||
}
|
|
@ -6,17 +6,16 @@
|
|||
*/
|
||||
package org.hibernate.orm.test.metamodel.mapping;
|
||||
|
||||
import java.sql.Statement;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.internal.BasicValuedSingularAttributeMapping;
|
||||
|
@ -31,10 +30,11 @@ import org.hibernate.usertype.UserType;
|
|||
|
||||
import org.hibernate.testing.hamcrest.CollectionMatchers;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.FailureExpected;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
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.CoreMatchers.instanceOf;
|
||||
|
@ -47,7 +47,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
|||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@DomainModel(
|
||||
annotatedClasses = SmokeTests.SimpleEntity.class
|
||||
annotatedClasses = { SmokeTests.SimpleEntity.class, SmokeTests.OtherEntity.class }
|
||||
)
|
||||
@ServiceRegistry
|
||||
@SessionFactory
|
||||
|
@ -60,7 +60,9 @@ public class SmokeTests {
|
|||
.getEntityDescriptor( SimpleEntity.class );
|
||||
|
||||
final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping();
|
||||
assert Integer.class.equals( identifierMapping.getMappedTypeDescriptor().getMappedJavaTypeDescriptor().getJavaType() );
|
||||
assert Integer.class.equals( identifierMapping.getMappedTypeDescriptor()
|
||||
.getMappedJavaTypeDescriptor()
|
||||
.getJavaType() );
|
||||
|
||||
{
|
||||
final ModelPart namePart = entityDescriptor.findSubPart( "name" );
|
||||
|
@ -110,30 +112,71 @@ public class SmokeTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
@FailureExpected
|
||||
public void testEntityBasedManyToOne(SessionFactoryScope scope) {
|
||||
final EntityPersister entityDescriptor = scope.getSessionFactory()
|
||||
.getDomainModel()
|
||||
.getEntityDescriptor( OtherEntity.class );
|
||||
|
||||
final EntityPersister simpleEntityDescriptor = scope.getSessionFactory()
|
||||
.getDomainModel()
|
||||
.getEntityDescriptor( SimpleEntity.class );
|
||||
|
||||
final ModelPart part = entityDescriptor.findSubPart( "simpleEntity" );
|
||||
assertThat( part, notNullValue() );
|
||||
assertThat( part, instanceOf( SingularAssociationAttributeMapping.class ) );
|
||||
final SingularAssociationAttributeMapping attrMapping = (SingularAssociationAttributeMapping) part;
|
||||
// assertThat( attrMapping.getContainingTableExpression(), is( "mapping_simple_entity" ) );
|
||||
// assertThat( attrMapping.getMappedColumnExpressions(), CollectionMatchers.hasSize( 4 ) );
|
||||
// assertThat( attrMapping.getMappedColumnExpressions().get( 0 ), is( "attribute1" ) );
|
||||
// assertThat( attrMapping.getMappedColumnExpressions().get( 1 ), is( "attribute2" ) );
|
||||
assertThat( attrMapping.getAttributeName(), is( "simpleEntity" ) );
|
||||
assertThat( attrMapping.getMappedTypeDescriptor(), is( simpleEntityDescriptor ) );
|
||||
assertThat(
|
||||
attrMapping.getJavaTypeDescriptor(),
|
||||
is( simpleEntityDescriptor.getJavaTypeDescriptor() )
|
||||
);
|
||||
|
||||
assertThat( attrMapping.getDeclaringType(), is( entityDescriptor ) );
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
SimpleEntity simpleEntity = new SimpleEntity();
|
||||
simpleEntity.setId( 1 );
|
||||
simpleEntity.setGender( Gender.FEMALE );
|
||||
simpleEntity.setName( "Fab" );
|
||||
simpleEntity.setGender2( Gender.MALE );
|
||||
simpleEntity.setComponent( new Component( "a1", "a2" ) );
|
||||
session.save( simpleEntity );
|
||||
OtherEntity otherEntity = new OtherEntity();
|
||||
otherEntity.setId( 2 );
|
||||
otherEntity.setName( "Bar" );
|
||||
otherEntity.setSimpleEntity( simpleEntity );
|
||||
session.save( otherEntity );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session ->
|
||||
session.doWork(
|
||||
work -> {
|
||||
Statement statement = work.createStatement();
|
||||
statement.execute( "delete from mapping_other_entity" );
|
||||
statement.execute( "delete from mapping_simple_entity" );
|
||||
statement.close();
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public enum Gender {
|
||||
MALE,
|
||||
FEMALE
|
||||
}
|
||||
|
||||
@Entity( name = "OtherEntity" )
|
||||
@Table( name = "mapping_other_entity" )
|
||||
@Entity(name = "OtherEntity")
|
||||
@Table(name = "mapping_other_entity")
|
||||
@SuppressWarnings("unused")
|
||||
public static class OtherEntity {
|
||||
private Integer id;
|
||||
|
@ -167,8 +210,8 @@ public class SmokeTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Entity( name = "SimpleEntity" )
|
||||
@Table( name = "mapping_simple_entity" )
|
||||
@Entity(name = "SimpleEntity")
|
||||
@Table(name = "mapping_simple_entity")
|
||||
@SuppressWarnings("unused")
|
||||
public static class SimpleEntity {
|
||||
private Integer id;
|
||||
|
@ -203,7 +246,7 @@ public class SmokeTests {
|
|||
this.gender = gender;
|
||||
}
|
||||
|
||||
@Enumerated( EnumType.STRING )
|
||||
@Enumerated(EnumType.STRING)
|
||||
public Gender getGender2() {
|
||||
return gender2;
|
||||
}
|
||||
|
@ -222,7 +265,8 @@ public class SmokeTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Embeddable static class SubComponent {
|
||||
@Embeddable
|
||||
static class SubComponent {
|
||||
private String subAttribute1;
|
||||
private String subAttribute2;
|
||||
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* 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.sql.Statement;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
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.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@DomainModel(
|
||||
annotatedClasses = { ManyToOneTest.SimpleEntity.class, ManyToOneTest.OtherEntity.class }
|
||||
)
|
||||
@ServiceRegistry
|
||||
@SessionFactory
|
||||
public class ManyToOneTest {
|
||||
|
||||
@Test
|
||||
public void testSelect(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
OtherEntity otherEntity = session.
|
||||
createQuery( "from OtherEntity", OtherEntity.class )
|
||||
.uniqueResult();
|
||||
|
||||
assertThat( otherEntity.getName(), is( "Bar" ) );
|
||||
assertFalse( Hibernate.isInitialized( otherEntity.getSimpleEntity() ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectWithFecthJoin(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
OtherEntity otherEntity = session.
|
||||
createQuery( "from OtherEntity o join fetch o.simpleEntity", OtherEntity.class )
|
||||
.uniqueResult();
|
||||
|
||||
assertThat( otherEntity.getName(), is( "Bar" ) );
|
||||
assertTrue( Hibernate.isInitialized( otherEntity.getSimpleEntity() ) );
|
||||
assertThat( otherEntity.getSimpleEntity(), notNullValue() );
|
||||
assertThat( otherEntity.getSimpleEntity().getName(), is( "Fab" ) );
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
SimpleEntity simpleEntity = new SimpleEntity();
|
||||
simpleEntity.setId( 1 );
|
||||
session.save( simpleEntity );
|
||||
OtherEntity otherEntity = new OtherEntity();
|
||||
otherEntity.setId( 2 );
|
||||
otherEntity.setName( "Bar" );
|
||||
otherEntity.setSimpleEntity( simpleEntity );
|
||||
session.save( otherEntity );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session ->
|
||||
session.doWork(
|
||||
work -> {
|
||||
Statement statement = work.createStatement();
|
||||
statement.execute( "delete from mapping_other_entity" );
|
||||
statement.execute( "delete from mapping_simple_entity" );
|
||||
statement.close();
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "OtherEntity")
|
||||
@Table(name = "mapping_other_entity")
|
||||
public static class OtherEntity {
|
||||
private Integer id;
|
||||
private String name;
|
||||
private SimpleEntity simpleEntity;
|
||||
|
||||
@Id
|
||||
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;
|
||||
}
|
||||
|
||||
@ManyToOne
|
||||
public SimpleEntity getSimpleEntity() {
|
||||
return simpleEntity;
|
||||
}
|
||||
|
||||
public void setSimpleEntity(SimpleEntity simpleEntity) {
|
||||
this.simpleEntity = simpleEntity;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "SimpleEntity")
|
||||
@Table(name = "mapping_simple_entity")
|
||||
public static class SimpleEntity {
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
@Id
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -13,11 +13,16 @@ import java.util.concurrent.Callable;
|
|||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.orm.test.metamodel.mapping.SmokeTests.Component;
|
||||
import org.hibernate.orm.test.metamodel.mapping.SmokeTests.Gender;
|
||||
import org.hibernate.orm.test.metamodel.mapping.SmokeTests.OtherEntity;
|
||||
import org.hibernate.orm.test.metamodel.mapping.SmokeTests.SimpleEntity;
|
||||
import org.hibernate.query.Query;
|
||||
|
@ -37,16 +42,14 @@ import static org.hamcrest.CoreMatchers.instanceOf;
|
|||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hibernate.orm.test.metamodel.mapping.SmokeTests.Gender.FEMALE;
|
||||
import static org.hibernate.orm.test.metamodel.mapping.SmokeTests.Gender.MALE;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "DefaultAnnotationParam"})
|
||||
@SuppressWarnings({ "WeakerAccess", "DefaultAnnotationParam" })
|
||||
@DomainModel(
|
||||
annotatedClasses = {SimpleEntity.class, OtherEntity.class},
|
||||
annotatedClasses = { SmokeTests.SimpleEntity.class, SmokeTests.OtherEntity.class },
|
||||
extraQueryImportClasses = {
|
||||
SmokeTests.ListItemDto.class,
|
||||
SmokeTests.CategorizedListItemDto.class,
|
||||
|
@ -56,11 +59,11 @@ import static org.hibernate.orm.test.metamodel.mapping.SmokeTests.Gender.MALE;
|
|||
)
|
||||
@ServiceRegistry(
|
||||
settings = {
|
||||
@ServiceRegistry.Setting( name = AvailableSettings.POOL_SIZE, value = "15" ),
|
||||
@ServiceRegistry.Setting( name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "false" )
|
||||
@ServiceRegistry.Setting(name = AvailableSettings.POOL_SIZE, value = "15"),
|
||||
@ServiceRegistry.Setting(name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "false")
|
||||
}
|
||||
)
|
||||
@SessionFactory( exportSchema = true )
|
||||
@SessionFactory(exportSchema = true)
|
||||
public class SmokeTests {
|
||||
|
||||
@BeforeEach
|
||||
|
@ -69,11 +72,15 @@ public class SmokeTests {
|
|||
session -> {
|
||||
SimpleEntity simpleEntity = new SimpleEntity();
|
||||
simpleEntity.setId( 1 );
|
||||
simpleEntity.setGender( FEMALE );
|
||||
simpleEntity.setGender( Gender.FEMALE );
|
||||
simpleEntity.setName( "Fab" );
|
||||
simpleEntity.setGender2( MALE );
|
||||
simpleEntity.setGender2( Gender.MALE );
|
||||
simpleEntity.setComponent( new Component( "a1", "a2" ) );
|
||||
session.save( simpleEntity );
|
||||
OtherEntity otherEntity = new OtherEntity();
|
||||
otherEntity.setId( 2 );
|
||||
otherEntity.setName( "Bar" );
|
||||
session.save( otherEntity );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -86,6 +93,7 @@ public class SmokeTests {
|
|||
work -> {
|
||||
Statement statement = work.createStatement();
|
||||
statement.execute( "delete from mapping_simple_entity" );
|
||||
statement.execute( "delete from mapping_other_entity" );
|
||||
statement.close();
|
||||
}
|
||||
)
|
||||
|
@ -117,7 +125,7 @@ public class SmokeTests {
|
|||
);
|
||||
List<Gender> simpleEntities = query.list();
|
||||
assertThat( simpleEntities.size(), is( 1 ) );
|
||||
assertThat( simpleEntities.get( 0 ), is( FEMALE ) );
|
||||
assertThat( simpleEntities.get( 0 ), is( Gender.FEMALE ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -133,10 +141,10 @@ public class SmokeTests {
|
|||
List<SimpleEntity> simpleEntities = query.list();
|
||||
assertThat( simpleEntities.size(), is( 1 ) );
|
||||
SimpleEntity simpleEntity = simpleEntities.get( 0 );
|
||||
assertThat( simpleEntity.getId(), is(1) );
|
||||
assertThat( simpleEntity.getGender(), is(FEMALE) );
|
||||
assertThat( simpleEntity.getGender2(), is(MALE) );
|
||||
assertThat( simpleEntity.getName(), is("Fab") );
|
||||
assertThat( simpleEntity.getId(), is( 1 ) );
|
||||
assertThat( simpleEntity.getGender(), is( Gender.FEMALE ) );
|
||||
assertThat( simpleEntity.getGender2(), is( Gender.MALE ) );
|
||||
assertThat( simpleEntity.getName(), is( "Fab" ) );
|
||||
assertThat( simpleEntity.getComponent(), notNullValue() );
|
||||
assertThat( simpleEntity.getComponent().getAttribute1(), is( "a1" ) );
|
||||
assertThat( simpleEntity.getComponent().getAttribute2(), is( "a2" ) );
|
||||
|
@ -148,7 +156,10 @@ public class SmokeTests {
|
|||
public void testHqlSelectEmbeddedAttribute(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<Component> query = session.createQuery( "select e.component from SimpleEntity e", Component.class );
|
||||
final QueryImplementor<Component> query = session.createQuery(
|
||||
"select e.component from SimpleEntity e",
|
||||
Component.class
|
||||
);
|
||||
final Component component = query.uniqueResult();
|
||||
assertThat( component, notNullValue() );
|
||||
assertThat( component.getAttribute1(), is( "a1" ) );
|
||||
|
@ -161,7 +172,10 @@ public class SmokeTests {
|
|||
public void testHqlSelectEmbeddableSubAttribute(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<String> query = session.createQuery( "select e.component.attribute1 from SimpleEntity e", String.class );
|
||||
final QueryImplementor<String> query = session.createQuery(
|
||||
"select e.component.attribute1 from SimpleEntity e",
|
||||
String.class
|
||||
);
|
||||
final String attribute1 = query.uniqueResult();
|
||||
assertThat( attribute1, is( "a1" ) );
|
||||
}
|
||||
|
@ -172,7 +186,10 @@ public class SmokeTests {
|
|||
public void testHqlSelectLiteral(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<String> query = session.createQuery( "select 'items' from SimpleEntity e", String.class );
|
||||
final QueryImplementor<String> query = session.createQuery(
|
||||
"select 'items' from SimpleEntity e",
|
||||
String.class
|
||||
);
|
||||
final String attribute1 = query.uniqueResult();
|
||||
assertThat( attribute1, is( "items" ) );
|
||||
}
|
||||
|
@ -183,7 +200,10 @@ public class SmokeTests {
|
|||
public void testHqlSelectParameter(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<String> query = session.createQuery( "select :param from SimpleEntity e", String.class );
|
||||
final QueryImplementor<String> query = session.createQuery(
|
||||
"select :param from SimpleEntity e",
|
||||
String.class
|
||||
);
|
||||
final String attribute1 = query.setParameter( "param", "items" ).uniqueResult();
|
||||
assertThat( attribute1, is( "items" ) );
|
||||
}
|
||||
|
@ -215,9 +235,9 @@ public class SmokeTests {
|
|||
session -> {
|
||||
SimpleEntity simpleEntity = new SimpleEntity();
|
||||
simpleEntity.setId( 2 );
|
||||
simpleEntity.setGender( MALE );
|
||||
simpleEntity.setGender( Gender.MALE );
|
||||
simpleEntity.setName( "Andrea" );
|
||||
simpleEntity.setGender2( FEMALE );
|
||||
simpleEntity.setGender2( Gender.FEMALE );
|
||||
simpleEntity.setComponent( new Component( "b1", "b2" ) );
|
||||
session.save( simpleEntity );
|
||||
}
|
||||
|
@ -225,7 +245,10 @@ public class SmokeTests {
|
|||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final Object[] result = session.createQuery( "select e, e2 from SimpleEntity e, SimpleEntity e2 where e.id = 1 and e2.id = 2", Object[].class )
|
||||
final Object[] result = session.createQuery(
|
||||
"select e, e2 from SimpleEntity e, SimpleEntity e2 where e.id = 1 and e2.id = 2",
|
||||
Object[].class
|
||||
)
|
||||
.uniqueResult();
|
||||
assertThat( result, notNullValue() );
|
||||
|
||||
|
@ -247,9 +270,9 @@ public class SmokeTests {
|
|||
session -> {
|
||||
SimpleEntity simpleEntity = new SimpleEntity();
|
||||
simpleEntity.setId( 2 );
|
||||
simpleEntity.setGender( MALE );
|
||||
simpleEntity.setGender( Gender.MALE );
|
||||
simpleEntity.setName( "Andrea" );
|
||||
simpleEntity.setGender2( FEMALE );
|
||||
simpleEntity.setGender2( Gender.FEMALE );
|
||||
simpleEntity.setComponent( new Component( "b1", "b2" ) );
|
||||
session.save( simpleEntity );
|
||||
}
|
||||
|
@ -432,8 +455,8 @@ public class SmokeTests {
|
|||
final CategorizedListItemDto dto = query.getSingleResult();
|
||||
assertThat( dto, notNullValue() );
|
||||
assertThat( dto.category, notNullValue() );
|
||||
assertThat( dto.category.code, is( "a2") );
|
||||
assertThat( dto.category.value, is( "a1") );
|
||||
assertThat( dto.category.code, is( "a2" ) );
|
||||
assertThat( dto.category.value, is( "a1" ) );
|
||||
assertThat( dto.code, is( "a1" ) );
|
||||
assertThat( dto.value, is( "Fab" ) );
|
||||
}
|
||||
|
@ -452,8 +475,8 @@ public class SmokeTests {
|
|||
final CategorizedListItemDto dto = query.getSingleResult();
|
||||
assertThat( dto, notNullValue() );
|
||||
assertThat( dto.category, notNullValue() );
|
||||
assertThat( dto.category.code, is( "items") );
|
||||
assertThat( dto.category.value, is( "a1") );
|
||||
assertThat( dto.category.code, is( "items" ) );
|
||||
assertThat( dto.category.value, is( "a1" ) );
|
||||
assertThat( dto.code, is( "a2" ) );
|
||||
assertThat( dto.value, is( "Fab" ) );
|
||||
}
|
||||
|
@ -500,4 +523,162 @@ public class SmokeTests {
|
|||
}
|
||||
);
|
||||
}
|
||||
|
||||
public enum Gender {
|
||||
MALE,
|
||||
FEMALE
|
||||
}
|
||||
|
||||
@Entity(name = "OtherEntity")
|
||||
@Table(name = "mapping_other_entity")
|
||||
@SuppressWarnings("unused")
|
||||
public static class OtherEntity {
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
@Id
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Entity(name = "SimpleEntity")
|
||||
@Table(name = "mapping_simple_entity")
|
||||
@SuppressWarnings("unused")
|
||||
public static class SimpleEntity {
|
||||
private Integer id;
|
||||
private String name;
|
||||
private Gender gender;
|
||||
private Gender gender2;
|
||||
private Component component;
|
||||
|
||||
@Id
|
||||
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;
|
||||
}
|
||||
|
||||
@Enumerated
|
||||
public Gender getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
||||
public void setGender(Gender gender) {
|
||||
this.gender = gender;
|
||||
}
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
public Gender getGender2() {
|
||||
return gender2;
|
||||
}
|
||||
|
||||
public void setGender2(Gender gender2) {
|
||||
this.gender2 = gender2;
|
||||
}
|
||||
|
||||
@Embedded
|
||||
public Component getComponent() {
|
||||
return component;
|
||||
}
|
||||
|
||||
public void setComponent(Component component) {
|
||||
this.component = component;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
static class SubComponent {
|
||||
private String subAttribute1;
|
||||
private String subAttribute2;
|
||||
|
||||
public SubComponent() {
|
||||
}
|
||||
|
||||
public SubComponent(String subAttribute1, String subAttribute2) {
|
||||
this.subAttribute1 = subAttribute1;
|
||||
this.subAttribute2 = subAttribute2;
|
||||
}
|
||||
|
||||
public String getSubAttribute1() {
|
||||
return subAttribute1;
|
||||
}
|
||||
|
||||
public void setSubAttribute1(String subAttribute1) {
|
||||
this.subAttribute1 = subAttribute1;
|
||||
}
|
||||
|
||||
public String getSubAttribute2() {
|
||||
return subAttribute2;
|
||||
}
|
||||
|
||||
public void setSubAttribute2(String subAttribute2) {
|
||||
this.subAttribute2 = subAttribute2;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Component {
|
||||
private String attribute1;
|
||||
private String attribute2;
|
||||
|
||||
private SubComponent subComponent;
|
||||
|
||||
public Component() {
|
||||
}
|
||||
|
||||
public Component(String attribute1, String attribute2) {
|
||||
this.attribute1 = attribute1;
|
||||
this.attribute2 = attribute2;
|
||||
}
|
||||
|
||||
public String getAttribute1() {
|
||||
return attribute1;
|
||||
}
|
||||
|
||||
public void setAttribute1(String attribute1) {
|
||||
this.attribute1 = attribute1;
|
||||
}
|
||||
|
||||
public String getAttribute2() {
|
||||
return attribute2;
|
||||
}
|
||||
|
||||
public void setAttribute2(String attribute2) {
|
||||
this.attribute2 = attribute2;
|
||||
}
|
||||
|
||||
@Embedded
|
||||
public SubComponent getSubComponent() {
|
||||
return subComponent;
|
||||
}
|
||||
|
||||
public void setSubComponent(SubComponent subComponent) {
|
||||
this.subComponent = subComponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue