HHH-15875 Fix join fetch support for associations within embedded ids
This commit is contained in:
parent
2271e18ba5
commit
a71e26e333
|
@ -362,7 +362,6 @@ public class ToOneAttributeMapping
|
|||
isInternalLoadNullable = isNullable();
|
||||
}
|
||||
|
||||
|
||||
if ( referencedPropertyName == null ) {
|
||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||
targetKeyPropertyNames.add( EntityIdentifierMapping.ROLE_LOCAL_NAME );
|
||||
|
@ -380,18 +379,12 @@ public class ToOneAttributeMapping
|
|||
if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded()
|
||||
&& compositeType.getPropertyNames().length == 1 ) {
|
||||
this.targetKeyPropertyName = compositeType.getPropertyNames()[0];
|
||||
addPrefixedPropertyNames(
|
||||
addPrefixedPropertyPaths(
|
||||
targetKeyPropertyNames,
|
||||
targetKeyPropertyName,
|
||||
compositeType.getSubtypes()[0],
|
||||
declaringEntityPersister.getFactory()
|
||||
);
|
||||
addPrefixedPropertyNames(
|
||||
targetKeyPropertyNames,
|
||||
ForeignKeyDescriptor.PART_NAME,
|
||||
compositeType.getSubtypes()[0],
|
||||
declaringEntityPersister.getFactory()
|
||||
);
|
||||
}
|
||||
else {
|
||||
this.targetKeyPropertyName = EntityIdentifierMapping.ROLE_LOCAL_NAME;
|
||||
|
@ -401,52 +394,34 @@ public class ToOneAttributeMapping
|
|||
propertyType,
|
||||
declaringEntityPersister.getFactory()
|
||||
);
|
||||
addPrefixedPropertyNames(
|
||||
addPrefixedPropertyPaths(
|
||||
targetKeyPropertyNames,
|
||||
targetKeyPropertyName,
|
||||
propertyType,
|
||||
declaringEntityPersister.getFactory()
|
||||
);
|
||||
addPrefixedPropertyNames(
|
||||
targetKeyPropertyNames,
|
||||
ForeignKeyDescriptor.PART_NAME,
|
||||
propertyType,
|
||||
declaringEntityPersister.getFactory()
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.targetKeyPropertyName = entityBinding.getIdentifierProperty().getName();
|
||||
addPrefixedPropertyNames(
|
||||
addPrefixedPropertyPaths(
|
||||
targetKeyPropertyNames,
|
||||
targetKeyPropertyName,
|
||||
propertyType,
|
||||
declaringEntityPersister.getFactory()
|
||||
);
|
||||
addPrefixedPropertyNames(
|
||||
targetKeyPropertyNames,
|
||||
ForeignKeyDescriptor.PART_NAME,
|
||||
propertyType,
|
||||
declaringEntityPersister.getFactory()
|
||||
);
|
||||
}
|
||||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||
}
|
||||
else if ( bootValue.isReferenceToPrimaryKey() ) {
|
||||
this.targetKeyPropertyName = referencedPropertyName;
|
||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||
addPrefixedPropertyNames(
|
||||
addPrefixedPropertyPaths(
|
||||
targetKeyPropertyNames,
|
||||
targetKeyPropertyName,
|
||||
bootValue.getType(),
|
||||
declaringEntityPersister.getFactory()
|
||||
);
|
||||
addPrefixedPropertyNames(
|
||||
targetKeyPropertyNames,
|
||||
ForeignKeyDescriptor.PART_NAME,
|
||||
bootValue.getType(),
|
||||
declaringEntityPersister.getFactory()
|
||||
);
|
||||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||
}
|
||||
else {
|
||||
|
@ -458,18 +433,12 @@ public class ToOneAttributeMapping
|
|||
&& compositeType.getPropertyNames().length == 1 ) {
|
||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||
this.targetKeyPropertyName = compositeType.getPropertyNames()[0];
|
||||
addPrefixedPropertyNames(
|
||||
addPrefixedPropertyPaths(
|
||||
targetKeyPropertyNames,
|
||||
targetKeyPropertyName,
|
||||
compositeType.getSubtypes()[0],
|
||||
declaringEntityPersister.getFactory()
|
||||
);
|
||||
addPrefixedPropertyNames(
|
||||
targetKeyPropertyNames,
|
||||
ForeignKeyDescriptor.PART_NAME,
|
||||
compositeType.getSubtypes()[0],
|
||||
declaringEntityPersister.getFactory()
|
||||
);
|
||||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||
}
|
||||
else {
|
||||
|
@ -480,18 +449,12 @@ public class ToOneAttributeMapping
|
|||
if ( ( mapsIdAttributeName = findMapsIdPropertyName( entityMappingType, referencedPropertyName ) ) != null ) {
|
||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||
targetKeyPropertyNames.add( targetKeyPropertyName );
|
||||
addPrefixedPropertyNames(
|
||||
addPrefixedPropertyPaths(
|
||||
targetKeyPropertyNames,
|
||||
mapsIdAttributeName,
|
||||
entityMappingType.getEntityPersister().getIdentifierType(),
|
||||
declaringEntityPersister.getFactory()
|
||||
);
|
||||
addPrefixedPropertyNames(
|
||||
targetKeyPropertyNames,
|
||||
ForeignKeyDescriptor.PART_NAME,
|
||||
entityMappingType.getEntityPersister().getIdentifierType(),
|
||||
declaringEntityPersister.getFactory()
|
||||
);
|
||||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||
}
|
||||
else {
|
||||
|
@ -666,6 +629,31 @@ public class ToOneAttributeMapping
|
|||
return null;
|
||||
}
|
||||
|
||||
private static void addPrefixedPropertyPaths(
|
||||
Set<String> targetKeyPropertyNames,
|
||||
String prefix,
|
||||
Type type,
|
||||
SessionFactoryImplementor factory) {
|
||||
addPrefixedPropertyNames(
|
||||
targetKeyPropertyNames,
|
||||
prefix,
|
||||
type,
|
||||
factory
|
||||
);
|
||||
addPrefixedPropertyNames(
|
||||
targetKeyPropertyNames,
|
||||
ForeignKeyDescriptor.PART_NAME,
|
||||
type,
|
||||
factory
|
||||
);
|
||||
addPrefixedPropertyNames(
|
||||
targetKeyPropertyNames,
|
||||
EntityIdentifierMapping.ROLE_LOCAL_NAME,
|
||||
type,
|
||||
factory
|
||||
);
|
||||
}
|
||||
|
||||
public static void addPrefixedPropertyNames(
|
||||
Set<String> targetKeyPropertyNames,
|
||||
String prefix,
|
||||
|
|
|
@ -13,20 +13,25 @@ import java.util.function.Supplier;
|
|||
import org.hibernate.graph.spi.GraphHelper;
|
||||
import org.hibernate.metamodel.AttributeClassification;
|
||||
import org.hibernate.metamodel.internal.MetadataContext;
|
||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||
import org.hibernate.metamodel.model.domain.AnyMappingDomainType;
|
||||
import org.hibernate.metamodel.model.domain.DomainType;
|
||||
import org.hibernate.metamodel.model.domain.ManagedDomainType;
|
||||
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
|
||||
import org.hibernate.metamodel.model.domain.SimpleDomainType;
|
||||
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
|
||||
import org.hibernate.query.SemanticException;
|
||||
import org.hibernate.query.sqm.SqmPathSource;
|
||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||
import org.hibernate.query.sqm.internal.SqmMappingModelHelper;
|
||||
import org.hibernate.query.sqm.spi.SqmCreationHelper;
|
||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmSingularJoin;
|
||||
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
|
||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||
import org.hibernate.spi.EntityIdentifierNavigablePath;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
/**
|
||||
|
@ -162,6 +167,20 @@ public class SingularAttributeImpl<D,J>
|
|||
metadataContext
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath createNavigablePath(SqmPath parent, String alias) {
|
||||
if ( parent == null ) {
|
||||
throw new IllegalArgumentException(
|
||||
"`lhs` cannot be null for a sub-navigable reference - " + parent
|
||||
);
|
||||
}
|
||||
NavigablePath navigablePath = parent.getNavigablePath();
|
||||
if ( parent.getReferencedPathSource() instanceof PluralPersistentAttribute<?, ?, ?> ) {
|
||||
navigablePath = navigablePath.append( CollectionPart.Nature.ELEMENT.getName() );
|
||||
}
|
||||
return new EntityIdentifierNavigablePath( navigablePath, SqmCreationHelper.determineAlias( alias ), getName() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,10 +7,12 @@
|
|||
package org.hibernate.query.sqm;
|
||||
|
||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||
import org.hibernate.query.sqm.spi.SqmCreationHelper;
|
||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||
import org.hibernate.query.sqm.tree.from.SqmJoin;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
|
||||
/**
|
||||
* Specialization for attributes that that can be used in creating SQM joins
|
||||
|
@ -29,4 +31,8 @@ public interface SqmJoinable<O, E> {
|
|||
SqmCreationState creationState);
|
||||
|
||||
String getName();
|
||||
|
||||
default NavigablePath createNavigablePath(SqmPath<?> parent, String alias) {
|
||||
return SqmCreationHelper.buildSubNavigablePath( parent, getName(), alias );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ public class SqmCreationHelper {
|
|||
return lhs.append( base, determineAlias( alias ) );
|
||||
}
|
||||
|
||||
private static String determineAlias(String alias) {
|
||||
public static String determineAlias(String alias) {
|
||||
// Make sure we always create a unique alias, otherwise we might use a wrong table group for the same join
|
||||
if ( alias == null ) {
|
||||
return Long.toString( System.nanoTime() );
|
||||
|
|
|
@ -272,6 +272,7 @@ import org.hibernate.query.sqm.tree.select.SqmSubQuery;
|
|||
import org.hibernate.query.sqm.tree.update.SqmAssignment;
|
||||
import org.hibernate.query.sqm.tree.update.SqmSetClause;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
import org.hibernate.spi.EntityIdentifierNavigablePath;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
|
@ -382,6 +383,7 @@ import org.hibernate.sql.results.graph.Fetch;
|
|||
import org.hibernate.sql.results.graph.FetchParent;
|
||||
import org.hibernate.sql.results.graph.Fetchable;
|
||||
import org.hibernate.sql.results.graph.FetchableContainer;
|
||||
import org.hibernate.sql.results.graph.entity.EntityResultGraphNode;
|
||||
import org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiation;
|
||||
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
|
||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||
|
@ -7162,7 +7164,16 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
// .getOrMakeJavaDescriptor( namedClass );
|
||||
}
|
||||
|
||||
private void addFetch(ImmutableFetchList.Builder fetches, FetchParent fetchParent, Fetchable fetchable, Boolean isKeyFetchable) {
|
||||
@Override
|
||||
public Fetch visitIdentifierFetch(EntityResultGraphNode fetchParent) {
|
||||
final EntityIdentifierMapping identifierMapping = fetchParent.getEntityValuedModelPart()
|
||||
.getEntityMappingType()
|
||||
.getIdentifierMapping();
|
||||
final Fetchable fetchableIdentifierMapping = (Fetchable) identifierMapping;
|
||||
return createFetch( fetchParent, fetchableIdentifierMapping, true );
|
||||
}
|
||||
|
||||
private Fetch createFetch(FetchParent fetchParent, Fetchable fetchable, Boolean isKeyFetchable) {
|
||||
final NavigablePath resolvedNavigablePath = fetchParent.resolveNavigablePath( fetchable );
|
||||
final Map.Entry<Integer, List<SqlSelection>> sqlSelectionsToTrack = trackedFetchSelectionsForGroup.get( resolvedNavigablePath );
|
||||
final int sqlSelectionStartIndexForFetch;
|
||||
|
@ -7308,8 +7319,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
);
|
||||
|
||||
if ( biDirectionalFetch != null ) {
|
||||
fetches.add( biDirectionalFetch );
|
||||
return;
|
||||
return biDirectionalFetch;
|
||||
}
|
||||
}
|
||||
final Fetch fetch = buildFetch(
|
||||
|
@ -7344,8 +7354,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
currentBagRole = fetchable.getNavigableRole().getNavigableName();
|
||||
}
|
||||
}
|
||||
fetches.add( fetch );
|
||||
}
|
||||
return fetch;
|
||||
}
|
||||
finally {
|
||||
if ( incrementFetchDepth ) {
|
||||
|
@ -7374,10 +7384,16 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
final int size = referencedMappingContainer.getNumberOfFetchables();
|
||||
final ImmutableFetchList.Builder fetches = new ImmutableFetchList.Builder( referencedMappingContainer );
|
||||
for ( int i = 0; i < keySize; i++ ) {
|
||||
addFetch( fetches, fetchParent, referencedMappingContainer.getKeyFetchable( i ), true );
|
||||
final Fetch fetch = createFetch( fetchParent, referencedMappingContainer.getKeyFetchable( i ), true );
|
||||
if ( fetch != null ) {
|
||||
fetches.add( fetch );
|
||||
}
|
||||
}
|
||||
for ( int i = 0; i < size; i++ ) {
|
||||
addFetch( fetches, fetchParent, referencedMappingContainer.getFetchable( i ), false );
|
||||
final Fetch fetch = createFetch( fetchParent, referencedMappingContainer.getFetchable( i ), false );
|
||||
if ( fetch != null ) {
|
||||
fetches.add( fetch );
|
||||
}
|
||||
}
|
||||
return fetches.build();
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ public abstract class AbstractSqmAttributeJoin<O,T>
|
|||
NodeBuilder nodeBuilder) {
|
||||
this(
|
||||
lhs,
|
||||
SqmCreationHelper.buildSubNavigablePath( lhs, joinedNavigable.getName(), alias ),
|
||||
joinedNavigable.createNavigablePath( lhs, alias ),
|
||||
joinedNavigable,
|
||||
alias == SqmCreationHelper.IMPLICIT_ALIAS ? null : alias,
|
||||
joinType,
|
||||
|
|
|
@ -24,6 +24,11 @@ public class EntityIdentifierNavigablePath extends NavigablePath {
|
|||
this.identifierAttributeName = identifierAttributeName;
|
||||
}
|
||||
|
||||
public EntityIdentifierNavigablePath(NavigablePath parent, String alias, String identifierAttributeName) {
|
||||
super( parent, EntityIdentifierMapping.ROLE_LOCAL_NAME, alias );
|
||||
this.identifierAttributeName = identifierAttributeName;
|
||||
}
|
||||
|
||||
public String getIdentifierAttributeName() {
|
||||
return identifierAttributeName;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.hibernate.Incubating;
|
|||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
|
@ -36,7 +37,7 @@ public interface FetchParent extends DomainResultGraphNode {
|
|||
|
||||
default NavigablePath resolveNavigablePath(Fetchable fetchable) {
|
||||
final String fetchableName = fetchable.getFetchableName();
|
||||
if ( NavigablePath.IDENTIFIER_MAPPER_PROPERTY.equals( fetchableName ) ) {
|
||||
if ( NavigablePath.IDENTIFIER_MAPPER_PROPERTY.equals( fetchableName ) || fetchable instanceof EntityIdentifierMapping ) {
|
||||
return new EntityIdentifierNavigablePath( getNavigablePath(), fetchableName );
|
||||
}
|
||||
else {
|
||||
|
@ -53,8 +54,7 @@ public interface FetchParent extends DomainResultGraphNode {
|
|||
else {
|
||||
fetchParentType = fetchableEntityType;
|
||||
}
|
||||
if ( fetchParentType != fetchableEntityType ) {
|
||||
// todo (6.0): if the fetchParentType is a subtype of fetchableEntityType this shouldn't be necessary
|
||||
if ( fetchParentType != null && !fetchParentType.isTypeOrSuperType( fetchableEntityType ) ) {
|
||||
return getNavigablePath().treatAs( fetchableEntityType.getEntityName() )
|
||||
.append( fetchableName );
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ public class EntityResultImpl extends AbstractEntityResultGraphNode implements E
|
|||
for ( TableGroupJoin tableGroupJoin : tableGroup.getTableGroupJoins() ) {
|
||||
final NavigablePath navigablePath = tableGroupJoin.getNavigablePath();
|
||||
if ( tableGroupJoin.getJoinedGroup().isFetched()
|
||||
&& fetchable.getFetchableName().equals( navigablePath.getLocalName() )
|
||||
&& fetchable.getNavigableRole().getLocalName().equals( navigablePath.getLocalName() )
|
||||
&& tableGroupJoin.getJoinedGroup().getModelPart() == fetchable ) {
|
||||
return navigablePath;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.hibernate.graph.spi.GraphImplementor;
|
|||
import org.hibernate.graph.spi.RootGraphImplementor;
|
||||
import org.hibernate.graph.spi.SubGraphImplementor;
|
||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
|
||||
import org.hibernate.sql.results.graph.EntityGraphTraversalState;
|
||||
|
@ -46,6 +47,9 @@ public class StandardEntityGraphTraversalStateImpl implements EntityGraphTravers
|
|||
@Override
|
||||
public TraversalResult traverse(FetchParent fetchParent, Fetchable fetchable, boolean exploreKeySubgraph) {
|
||||
assert !(fetchable instanceof CollectionPart);
|
||||
if ( fetchable instanceof NonAggregatedIdentifierMapping ) {
|
||||
return new TraversalResult( currentGraphContext, FetchTiming.IMMEDIATE, true );
|
||||
}
|
||||
|
||||
final GraphImplementor previousContextRoot = currentGraphContext;
|
||||
AttributeNodeImplementor attributeNode = null;
|
||||
|
|
|
@ -0,0 +1,271 @@
|
|||
package org.hibernate.orm.test.jpa.cdi;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Jpa;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Fetch;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@Jpa(
|
||||
annotatedClasses = {
|
||||
FetchEmbeddedIdTest.User.class,
|
||||
FetchEmbeddedIdTest.GroupType.class,
|
||||
FetchEmbeddedIdTest.Group.class,
|
||||
FetchEmbeddedIdTest.UserGroup.class
|
||||
}
|
||||
)
|
||||
@TestForIssue( jiraKey = "HHH-15875")
|
||||
public class FetchEmbeddedIdTest {
|
||||
|
||||
@BeforeAll
|
||||
public void setUp(EntityManagerFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
entityManager -> {
|
||||
User user = new User( 1l, "user name" );
|
||||
|
||||
GroupType groupType = new GroupType( 1l, "group type" );
|
||||
Group group = new Group( 1l, "user group", groupType );
|
||||
|
||||
UserGroupId userGroupId = new UserGroupId( user, group );
|
||||
|
||||
UserGroup userGroup = new UserGroup( userGroupId, "value" );
|
||||
|
||||
entityManager.persist( groupType );
|
||||
entityManager.persist( group );
|
||||
entityManager.persist( user );
|
||||
entityManager.persist( userGroup );
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCriteriaFetch(EntityManagerFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
entityManager -> {
|
||||
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<UserGroup> query = criteriaBuilder.createQuery( UserGroup.class );
|
||||
|
||||
Root<UserGroup> root = query.from( UserGroup.class );
|
||||
|
||||
Fetch<?, ?> userGroupFetch = root.fetch( "userGroupId" );
|
||||
userGroupFetch.fetch( "user" );
|
||||
userGroupFetch.fetch( "group" ).fetch( "groupType" );
|
||||
|
||||
List<UserGroup> results = entityManager.createQuery( query ).getResultList();
|
||||
assertThat( results ).hasSize( 1 );
|
||||
|
||||
UserGroup userGroup = results.get( 0 );
|
||||
UserGroupId userGroupId = userGroup.getUserGroupId();
|
||||
Group group = userGroupId.getGroup();
|
||||
assertTrue( Hibernate.isInitialized( group ) );
|
||||
String name = group.getName();
|
||||
assertThat( name ).isEqualTo( "user group" );
|
||||
|
||||
User user = userGroupId.getUser();
|
||||
assertTrue( Hibernate.isInitialized( user ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHqlFetch(EntityManagerFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
entityManager -> {
|
||||
|
||||
List<UserGroup> results = entityManager.createQuery( "select ug from UserGroup ug join fetch ug.userGroupId ugi join fetch ugi.group join fetch ugi.user" ).getResultList();
|
||||
assertThat( results ).hasSize( 1 );
|
||||
|
||||
UserGroup userGroup = results.get( 0 );
|
||||
UserGroupId userGroupId = userGroup.getUserGroupId();
|
||||
Group group = userGroupId.getGroup();
|
||||
assertTrue( Hibernate.isInitialized( group ) );
|
||||
String name = group.getName();
|
||||
assertThat( name ).isEqualTo( "user group" );
|
||||
|
||||
User user = userGroupId.getUser();
|
||||
assertTrue( Hibernate.isInitialized( user ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "UserGroup")
|
||||
public static class UserGroup {
|
||||
|
||||
@EmbeddedId
|
||||
private UserGroupId userGroupId;
|
||||
|
||||
private String joinedPropertyValue;
|
||||
|
||||
public UserGroup() {
|
||||
}
|
||||
|
||||
public UserGroup(UserGroupId userGroupId, String joinedPropertyValue) {
|
||||
this.userGroupId = userGroupId;
|
||||
this.joinedPropertyValue = joinedPropertyValue;
|
||||
}
|
||||
|
||||
public UserGroupId getUserGroupId() {
|
||||
return userGroupId;
|
||||
}
|
||||
|
||||
public String getJoinedPropertyValue() {
|
||||
return joinedPropertyValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class UserGroupId implements Serializable {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
private User user;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
private Group group;
|
||||
|
||||
public UserGroupId() {
|
||||
}
|
||||
|
||||
public UserGroupId(User user, Group group) {
|
||||
this.user = user;
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public Group getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if ( this == object ) {
|
||||
return true;
|
||||
}
|
||||
if ( !( object instanceof UserGroupId ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UserGroupId that = (UserGroupId) object;
|
||||
|
||||
return Objects.equals( user.getId(), that.user.getId() ) && Objects.equals(
|
||||
group.getId(),
|
||||
that.group.getId()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash( user.getId(), group.getId() );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "User")
|
||||
@Table(name = "test_user")
|
||||
public static class User {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(Long id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "GROUP")
|
||||
@Table(name = "test_group")
|
||||
public static class Group {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
private GroupType groupType;
|
||||
|
||||
public Group() {
|
||||
}
|
||||
|
||||
public Group(Long id, String name, GroupType groupType) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.groupType = groupType;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public GroupType getGroupType() {
|
||||
return groupType;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "GroupType")
|
||||
public static class GroupType {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
public GroupType() {
|
||||
|
||||
}
|
||||
|
||||
public GroupType(Long id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue