HHH-13725 - Implement ToOne Associations support

This commit is contained in:
Andrea Boriero 2019-10-21 12:19:25 +02:00 committed by Steve Ebersole
parent 83a1eb5715
commit 403bf9257c
14 changed files with 1110 additions and 78 deletions

View File

@ -9,5 +9,6 @@ package org.hibernate.metamodel.mapping;
/**
* @author Steve Ebersole
*/
public interface SingularAttributeMapping extends AttributeMapping, StateArrayContributorMapping {
public interface SingularAttributeMapping
extends AttributeMapping, StateArrayContributorMapping {
}

View File

@ -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
);
}
}

View File

@ -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
);
}
}

View File

@ -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

View File

@ -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

View File

@ -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) {
}
}

View File

@ -115,7 +115,7 @@ public class DelayedCollectionAssembler implements DomainResultAssembler {
persistenceContext.addUninitializedCollection(
getInitializingCollectionDescriptor(),
instance,
(Serializable) collectionKey.getKey()
collectionKey.getKey()
);
}

View File

@ -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 {
}

View File

@ -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 );
}
}
}
}

View File

@ -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 );
}
}

View File

@ -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() ) + ")";
}
}

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}