From f07c26339bd0b176bb48888dd7af3800a811aa9b Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 1 Sep 2020 10:31:22 +0100 Subject: [PATCH 01/13] HHH-14199 fix the error when running 'setDataBase' gradle task --- gradle/java-module.gradle | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/gradle/java-module.gradle b/gradle/java-module.gradle index 895042178b..dfa9cadb78 100644 --- a/gradle/java-module.gradle +++ b/gradle/java-module.gradle @@ -303,16 +303,12 @@ task copyResourcesToIntelliJOutFolder { Afterward, you can run any test from the IDE against that particular DB. */ -task setDataBase { - inputs.property( "db", db ) - doLast { - processTestResources - copyResourcesToIntelliJOutFolder - - println( 'Setting current database to ' + db ) - } +task setDataBase dependsOn( processTestResources, copyResourcesToIntelliJOutFolder ) { + println( 'Setting current database to ' + db ) } +copyResourcesToIntelliJOutFolder.mustRunAfter processTestResources + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Report configs From efa7e66c73069c6651d2cf906033fa0643ac4005 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 1 Sep 2020 10:48:37 +0100 Subject: [PATCH 02/13] HHH-14178 Fix oracle test failures --- ...ewlyInstantiatdCollectionSkipDeleteOrphanTest.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/event/service/internal/NewlyInstantiatdCollectionSkipDeleteOrphanTest.java b/hibernate-core/src/test/java/org/hibernate/event/service/internal/NewlyInstantiatdCollectionSkipDeleteOrphanTest.java index 59e72494e5..08ae2f8ee5 100644 --- a/hibernate-core/src/test/java/org/hibernate/event/service/internal/NewlyInstantiatdCollectionSkipDeleteOrphanTest.java +++ b/hibernate-core/src/test/java/org/hibernate/event/service/internal/NewlyInstantiatdCollectionSkipDeleteOrphanTest.java @@ -27,6 +27,8 @@ import org.hibernate.Transaction; import org.hibernate.annotations.Cascade; import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.After; @@ -39,6 +41,7 @@ import org.junit.Test; * @author Nathan Xu */ @TestForIssue( jiraKey = "HHH-14178" ) +@RequiresDialectFeature(DialectChecks.SupportsIdentityColumns.class) public class NewlyInstantiatdCollectionSkipDeleteOrphanTest extends BaseCoreFunctionalTestCase { private UnversionedParent up; @@ -265,7 +268,7 @@ public class NewlyInstantiatdCollectionSkipDeleteOrphanTest extends BaseCoreFunc } @Entity(name = "UnversionedParent") - @Table(name = "UnversionedParent") + @Table(name = "UnversParent") @DynamicUpdate public static class UnversionedParent { private Integer id; @@ -346,7 +349,7 @@ public class NewlyInstantiatdCollectionSkipDeleteOrphanTest extends BaseCoreFunc } @Entity(name = "VersionedParent") - @Table(name = "VersionedParent") + @Table(name = "VersParent") @DynamicUpdate public static class VersionedParent { private Integer id; @@ -438,7 +441,7 @@ public class NewlyInstantiatdCollectionSkipDeleteOrphanTest extends BaseCoreFunc } @Entity(name = "VersionedMappingUnversionedParent") - @Table(name = "VersionedMappingUnversionedParent") + @Table(name = "VersdMapUnversParent") @DynamicUpdate public static class VersionedMappingUnversionedParent { private MappingId id; @@ -551,7 +554,7 @@ public class NewlyInstantiatdCollectionSkipDeleteOrphanTest extends BaseCoreFunc } @Entity(name = "VersionedMappingVersionedParent") - @Table(name = "VersionedMappingVersionedParent") + @Table(name = "VersMapVersParent") @DynamicUpdate public static class VersionedMappingVersionedParent { private MappingId id; From e8b6bbbef43a65215fea9b788f050626bf4aa668 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 1 Sep 2020 10:48:37 +0100 Subject: [PATCH 03/13] HHH-14178 Fix Hana test failures --- ...antiatdCollectionSkipDeleteOrphanTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/event/service/internal/NewlyInstantiatdCollectionSkipDeleteOrphanTest.java b/hibernate-core/src/test/java/org/hibernate/event/service/internal/NewlyInstantiatdCollectionSkipDeleteOrphanTest.java index 08ae2f8ee5..c27d7fe3f3 100644 --- a/hibernate-core/src/test/java/org/hibernate/event/service/internal/NewlyInstantiatdCollectionSkipDeleteOrphanTest.java +++ b/hibernate-core/src/test/java/org/hibernate/event/service/internal/NewlyInstantiatdCollectionSkipDeleteOrphanTest.java @@ -180,6 +180,8 @@ public class NewlyInstantiatdCollectionSkipDeleteOrphanTest extends BaseCoreFunc private Integer id; private Long version; + private String name; + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "Id", nullable = false) @@ -191,6 +193,14 @@ public class NewlyInstantiatdCollectionSkipDeleteOrphanTest extends BaseCoreFunc this.id = id; } + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + @Version @Column(name = "Version", nullable = false) public Long getVersion() { @@ -274,6 +284,8 @@ public class NewlyInstantiatdCollectionSkipDeleteOrphanTest extends BaseCoreFunc private Integer id; private Set versionedMappings; + private String name; + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name="Id", nullable=false) @@ -294,6 +306,14 @@ public class NewlyInstantiatdCollectionSkipDeleteOrphanTest extends BaseCoreFunc } } + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + @OneToMany(mappedBy="parent", cascade={ javax.persistence.CascadeType.DETACH, javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.REFRESH, javax.persistence.CascadeType.REMOVE }, orphanRemoval=true) @Cascade({ org.hibernate.annotations.CascadeType.DELETE, org.hibernate.annotations.CascadeType.LOCK, org.hibernate.annotations.CascadeType.REPLICATE }) protected Set getVersionedMappings() { @@ -355,6 +375,7 @@ public class NewlyInstantiatdCollectionSkipDeleteOrphanTest extends BaseCoreFunc private Integer id; private Long version; private Set children; + private String name; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -376,6 +397,14 @@ public class NewlyInstantiatdCollectionSkipDeleteOrphanTest extends BaseCoreFunc } } + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + @Version @Column(name="Version", nullable=false) public Long getVersion() { @@ -447,6 +476,7 @@ public class NewlyInstantiatdCollectionSkipDeleteOrphanTest extends BaseCoreFunc private MappingId id; private Child child; private Long version; + private String name; @EmbeddedId public MappingId getId() { @@ -467,6 +497,14 @@ public class NewlyInstantiatdCollectionSkipDeleteOrphanTest extends BaseCoreFunc this.version = version; } + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + protected UnversionedParent parent; @ManyToOne(optional=false, fetch=FetchType.LAZY) @@ -560,6 +598,7 @@ public class NewlyInstantiatdCollectionSkipDeleteOrphanTest extends BaseCoreFunc private MappingId id; private Child child; private Long version; + private String name; @EmbeddedId public MappingId getId() { @@ -580,6 +619,14 @@ public class NewlyInstantiatdCollectionSkipDeleteOrphanTest extends BaseCoreFunc this.version = version; } + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + protected VersionedParent parent; @ManyToOne(optional=false, fetch=FetchType.LAZY) From 162bc7d9dd671d6fc3c596a2ec29c469c1acb7b3 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Wed, 22 Jul 2020 18:52:00 -0400 Subject: [PATCH 04/13] HHH-14113 Entity Graph attribute resolution not based on selected entity --- .../query/spi/EntityGraphQueryHint.java | 17 +- .../EntityGraphAttributeResolutionTest.java | 254 ++++++++++++++++++ 2 files changed, 268 insertions(+), 3 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/jpa/test/graphs/EntityGraphAttributeResolutionTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/EntityGraphQueryHint.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/EntityGraphQueryHint.java index 239ff3c95a..d0387a1bbd 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/EntityGraphQueryHint.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/EntityGraphQueryHint.java @@ -18,7 +18,6 @@ import javax.persistence.Subgraph; import org.hibernate.QueryException; import org.hibernate.engine.internal.JoinSequence; import org.hibernate.graph.GraphSemantic; -import org.hibernate.graph.RootGraph; import org.hibernate.graph.spi.AppliedGraph; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.hql.internal.ast.HqlSqlWalker; @@ -26,6 +25,8 @@ import org.hibernate.hql.internal.ast.tree.FromClause; import org.hibernate.hql.internal.ast.tree.FromElement; import org.hibernate.hql.internal.ast.tree.FromElementFactory; import org.hibernate.hql.internal.ast.tree.ImpliedFromElement; +import org.hibernate.internal.CoreLogging; +import org.hibernate.internal.CoreMessageLogger; import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.sql.JoinType; import org.hibernate.type.CollectionType; @@ -42,6 +43,8 @@ import antlr.SemanticException; * @author Brett Meyer */ public class EntityGraphQueryHint implements AppliedGraph { + private static final CoreMessageLogger LOG = CoreLogging.messageLogger( EntityGraphQueryHint.class ); + private final RootGraphImplementor graph; private final GraphSemantic semantic; @@ -77,9 +80,17 @@ public class EntityGraphQueryHint implements AppliedGraph { } } + boolean applyEntityGraph = false; + if ( fromClause.getLevel() == FromClause.ROOT_LEVEL ) { + final String fromElementEntityName = fromClause.getFromElement().getEntityPersister().getEntityName(); + applyEntityGraph = graph.appliesTo( fromElementEntityName ); + if ( !applyEntityGraph ) { + LOG.warnf( "Entity graph is not applicable to the root entity [%s]; Ignored.", fromElementEntityName ); + } + } + return getFromElements( - fromClause.getLevel() == FromClause.ROOT_LEVEL ? graph.getAttributeNodes(): - Collections.emptyList(), + applyEntityGraph ? graph.getAttributeNodes() : Collections.emptyList(), fromClause.getFromElement(), fromClause, walker, diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/graphs/EntityGraphAttributeResolutionTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/graphs/EntityGraphAttributeResolutionTest.java new file mode 100644 index 0000000000..065ee60929 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/graphs/EntityGraphAttributeResolutionTest.java @@ -0,0 +1,254 @@ +package org.hibernate.jpa.test.graphs; + +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.persistence.CollectionTable; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.EntityGraph; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToMany; +import javax.persistence.NamedAttributeNode; +import javax.persistence.NamedEntityGraph; +import javax.persistence.Table; + +import org.hibernate.graph.GraphSemantic; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.testing.TestForIssue; +import org.junit.Before; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; + + +/** + * @author Benjamin M. + * @author Nathan Xu + */ +@TestForIssue( jiraKey = "HHH-14113" ) +@SuppressWarnings({ "unchecked", "rawtypes" }) +public class EntityGraphAttributeResolutionTest extends BaseEntityManagerFunctionalTestCase { + + private User u; + private Group g1, g2; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { User.class, Group.class }; + } + + @Before + public void setUp() { + doInJPA( this::entityManagerFactory, em -> { + g1 = new Group(); + g1.addPermission( Permission.BAR ); + em.persist( g1 ); + + g2 = new Group(); + g2.addPermission( Permission.BAZ ); + em.persist( g2 ); + + u = new User(); + em.persist( u ); + u.addGroup( g1 ); + u.addGroup( g2 ); + } ); + } + + @Test + public void fetchAssocWithNamedFetchGraph() { + doInJPA( this::entityManagerFactory, em -> { + List result = em.createQuery( "SELECT u.groups FROM User u WHERE u.id = ?1" ) + .setParameter(1, u.getId() ) + .setHint( GraphSemantic.FETCH.getJpaHintName(), em.getEntityGraph( Group.ENTITY_GRAPH ) ) + .getResultList(); + + assertThat( result ).containsExactlyInAnyOrder( g1, g2 ); + } ); + } + + @Test + public void fetchAssocWithNamedFetchGraphAndJoin() { + doInJPA( this::entityManagerFactory, em -> { + List result = em.createQuery( "SELECT g FROM User u JOIN u.groups g WHERE u.id = ?1" ) + .setParameter( 1, u.getId() ) + .setHint( GraphSemantic.FETCH.getJpaHintName(), em.getEntityGraph( Group.ENTITY_GRAPH ) ) + .getResultList(); + + assertThat( result ).containsExactlyInAnyOrder( g1, g2 ); + } ); + } + + @Test + public void fetchAssocWithAdhocFetchGraph() { + doInJPA( this::entityManagerFactory, em -> { + EntityGraph eg = em.createEntityGraph( Group.class ); + eg.addAttributeNodes( "permissions" ); + + List result = em.createQuery( "SELECT u.groups FROM User u WHERE u.id = ?1" ) + .setParameter(1, u.getId() ) + .setHint( GraphSemantic.FETCH.getJpaHintName(), eg ) + .getResultList(); + + assertThat( result ).containsExactlyInAnyOrder( g1, g2 ); + } ); + } + + @Entity(name = "Group") + @NamedEntityGraph(name = Group.ENTITY_GRAPH, + attributeNodes = { + @NamedAttributeNode("permissions") + }) + @Table(name = "groups") // Name 'group' not accepted by H2 + public static class Group { + public static final String ENTITY_GRAPH = "group-with-permissions"; + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Enumerated(EnumType.STRING) + @ElementCollection(targetClass = Permission.class) + @CollectionTable( + name = "GROUPS_PERMISSIONS", + joinColumns = @JoinColumn(name = "gid") + ) + private Set permissions = EnumSet.noneOf( Permission.class ); + + public Group() {} + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Set getPermissions() { + return permissions; + } + + public void setPermissions(Set permissions) { + this.permissions = permissions; + } + + public void addPermission(Permission p) { + this.permissions.add( p ); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + + if ( !( o instanceof Group ) ) + return false; + + Group other = (Group) o; + + return id != null && + id.equals( other.getId() ); + } + + @Override + public int hashCode() { + return 31; + } + + @Override + public String toString() { + return "Group{" + + "id=" + id + + '}'; + } + } + + @Entity(name = "User") + @Table(name = "users") + public static class User { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Enumerated(EnumType.STRING) + @ElementCollection(targetClass = Permission.class) + @CollectionTable(name = "USERS_PERMISSIONS", joinColumns = @JoinColumn(name = "uid")) + private Set permissions = EnumSet.of( Permission.FOO ); + + @ManyToMany(fetch = FetchType.LAZY) + private Set groups = new HashSet<>(); + + public User() {} + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Set getPermissions() { + return permissions; + } + + public void setPermissions(Set permissions) { + this.permissions = permissions; + } + public void addPermission(Permission p) { + this.permissions.add( p ); + } + + public Set getGroups() { + return groups; + } + + public void setGroups(Set groups) { + this.groups = groups; + } + + public void addGroup(Group g) { + this.groups.add( g ); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + + if ( !( o instanceof User ) ) + return false; + + User other = (User) o; + + return id != null && + id.equals( other.getId() ); + } + + @Override + public int hashCode() { + return 31; + } + + @Override + public String toString() { + return "User{" + + "id=" + id + + '}'; + } + } + + public enum Permission { + FOO, BAR, BAZ + } +} From ea24abd75739db0546b2d4b190cfb650466eff4a Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Wed, 2 Sep 2020 12:16:41 +0100 Subject: [PATCH 05/13] HHH-9422 Metamodel Generator should close streams opened to persistence.xml and referenced mapping files --- .../jpamodelgen/xml/JpaDescriptorParser.java | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/xml/JpaDescriptorParser.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/xml/JpaDescriptorParser.java index 1de95db579..71cad464e8 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/xml/JpaDescriptorParser.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/xml/JpaDescriptorParser.java @@ -108,7 +108,7 @@ public class JpaDescriptorParser { private Persistence getPersistence() { Persistence persistence = null; String persistenceXmlLocation = context.getPersistenceXmlLocation(); - InputStream stream = xmlParserHelper.getInputStreamForResource( persistenceXmlLocation ); + final InputStream stream = xmlParserHelper.getInputStreamForResource( persistenceXmlLocation ); if ( stream == null ) { return null; } @@ -122,12 +122,13 @@ public class JpaDescriptorParser { Diagnostic.Kind.WARNING, "Unable to parse persistence.xml: " + e.getMessage() ); } - - try { - stream.close(); - } - catch (IOException e) { - // eat it + finally { + try { + stream.close(); + } + catch (IOException e) { + // eat it + } } return persistence; @@ -135,29 +136,29 @@ public class JpaDescriptorParser { private void loadEntityMappings(Collection mappingFileNames) { for ( String mappingFile : mappingFileNames ) { - InputStream stream = xmlParserHelper.getInputStreamForResource( mappingFile ); + final InputStream stream = xmlParserHelper.getInputStreamForResource( mappingFile ); if ( stream == null ) { continue; } - EntityMappings mapping = null; try { - Schema schema = xmlParserHelper.getSchema( ORM_SCHEMA ); - mapping = xmlParserHelper.getJaxbRoot( stream, EntityMappings.class, schema ); + final Schema schema = xmlParserHelper.getSchema( ORM_SCHEMA ); + final EntityMappings mapping = xmlParserHelper.getJaxbRoot( stream, EntityMappings.class, schema ); + if ( mapping != null ) { + entityMappings.add( mapping ); + } } catch (XmlParsingException e) { context.logMessage( Diagnostic.Kind.WARNING, "Unable to parse " + mappingFile + ": " + e.getMessage() ); } - if ( mapping != null ) { - entityMappings.add( mapping ); - } - - try { - stream.close(); - } - catch (IOException e) { - // eat it + finally { + try { + stream.close(); + } + catch (IOException e) { + // eat it + } } } } From 19af434b2124ef430bb762604d08868641b9f28d Mon Sep 17 00:00:00 2001 From: Moritz Becker Date: Sat, 12 Jan 2019 19:19:30 +0100 Subject: [PATCH 06/13] HHH-13201 - do not set empty text on collection join parent without queryable collection --- .../internal/ast/tree/FromElementFactory.java | 4 +- .../hql/internal/ast/tree/SelectClause.java | 2 +- ...veElementCollectionAndAssociationTest.java | 71 +++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/hql/FetchNonRootRelativeElementCollectionAndAssociationTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementFactory.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementFactory.java index 4f24021cb9..f304ff7123 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementFactory.java @@ -368,7 +368,9 @@ public class FromElementFactory implements SqlTokenTypes { // origin.addDestination( destination ); // This was the cause of HHH-242 // origin.setType( FROM_FRAGMENT ); // Set the parent node type so that the AST is properly formed. - origin.setText( "" ); // The destination node will have all the FROM text. + if ( origin.getQueryableCollection() != null ) { + origin.setText( "" ); // The destination node will have all the FROM text. + } origin.setCollectionJoin( true ); // The parent node is a collection join too (voodoo - see JoinProcessor) fromClause.addCollectionJoinFromElementByPath( path, destination ); fromClause.getWalker().addQuerySpaces( queryableCollection.getCollectionSpaces() ); diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java index 033ebbf1ab..93b8c17451 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java @@ -193,7 +193,7 @@ public class SelectClause extends SelectExpressionList { FromElement fromElement = (FromElement) iterator.next(); if ( fromElement.isFetch() ) { - FromElement origin = null; + FromElement origin; if ( fromElement.getRealOrigin() == null ) { // work around that crazy issue where the tree contains // "empty" FromElements (no text); afaict, this is caused diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/FetchNonRootRelativeElementCollectionAndAssociationTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/FetchNonRootRelativeElementCollectionAndAssociationTest.java new file mode 100644 index 0000000000..80b6523de5 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/FetchNonRootRelativeElementCollectionAndAssociationTest.java @@ -0,0 +1,71 @@ +package org.hibernate.test.hql; + +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +import javax.persistence.*; + +import java.util.HashMap; +import java.util.Map; + +import static javax.persistence.CascadeType.ALL; +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; + +/** + * @author Moritz Becker (moritz.becker@ordami.com) + * @date 12/01/2019 + * @company ordami GmbH + */ +@TestForIssue(jiraKey = "HHH-13201") +public class FetchNonRootRelativeElementCollectionAndAssociationTest extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { ProductNaturalId.class, Product.class, ProductDetail.class }; + } + + @Test + public void testJoinedSubclassUpdateWithCorrelation() { + doInJPA( this::entityManagerFactory, entityManager -> { + // DO NOT CHANGE this query: it used to trigger an error caused + // by the origin FromElement for the association fetch being resolved to the wrong FromElement due to the + // presence of an element collection join. + String u = "select prod from ProductNaturalId nat inner join nat.product prod " + + "left join fetch prod.productDetail " + + "left join fetch prod.normalizedPricesByUnit"; + Query query = entityManager.createQuery( u, Product.class ); + query.getResultList(); + } ); + } + + @Entity(name = "ProductNaturalId") + public class ProductNaturalId { + @Id + private String naturalId; + @OneToOne(optional = false) + private Product product; + } + + @Entity(name = "Product") + public class Product { + @Id + private Long id; + @OneToOne(mappedBy = "product", cascade = ALL, fetch = FetchType.LAZY) + private ProductDetail productDetail; + @OneToOne(mappedBy = "product", cascade = ALL, fetch = FetchType.LAZY) + private ProductNaturalId naturalId; + @ElementCollection(fetch = FetchType.LAZY) + private Map normalizedPricesByUnit = new HashMap<>(); + } + + @Entity(name = "ProductDetail") + public class ProductDetail { + @Id + private Long id; + @OneToOne(optional = false) + @JoinColumn(name = "id") + @MapsId + private Product product; + } +} From d97db034b4ef8a998e47d782527ce38e88ddb36f Mon Sep 17 00:00:00 2001 From: Moritz Becker Date: Tue, 15 Jan 2019 06:48:12 +0100 Subject: [PATCH 07/13] HHH-13201 - reformat test --- ...veElementCollectionAndAssociationTest.java | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/FetchNonRootRelativeElementCollectionAndAssociationTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/FetchNonRootRelativeElementCollectionAndAssociationTest.java index 80b6523de5..cf9e99cea7 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/FetchNonRootRelativeElementCollectionAndAssociationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/FetchNonRootRelativeElementCollectionAndAssociationTest.java @@ -1,6 +1,7 @@ package org.hibernate.test.hql; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + import org.hibernate.testing.TestForIssue; import org.junit.Test; @@ -13,14 +14,12 @@ import static javax.persistence.CascadeType.ALL; import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; /** - * @author Moritz Becker (moritz.becker@ordami.com) - * @date 12/01/2019 - * @company ordami GmbH + * @author Moritz Becker */ @TestForIssue(jiraKey = "HHH-13201") public class FetchNonRootRelativeElementCollectionAndAssociationTest extends BaseEntityManagerFunctionalTestCase { - @Override + @Override protected Class[] getAnnotatedClasses() { return new Class[] { ProductNaturalId.class, Product.class, ProductDetail.class }; } @@ -30,10 +29,10 @@ public class FetchNonRootRelativeElementCollectionAndAssociationTest extends Bas doInJPA( this::entityManagerFactory, entityManager -> { // DO NOT CHANGE this query: it used to trigger an error caused // by the origin FromElement for the association fetch being resolved to the wrong FromElement due to the - // presence of an element collection join. + // presence of an element collection join. String u = "select prod from ProductNaturalId nat inner join nat.product prod " + - "left join fetch prod.productDetail " + - "left join fetch prod.normalizedPricesByUnit"; + "left join fetch prod.productDetail " + + "left join fetch prod.normalizedPricesByUnit"; Query query = entityManager.createQuery( u, Product.class ); query.getResultList(); } ); @@ -41,31 +40,31 @@ public class FetchNonRootRelativeElementCollectionAndAssociationTest extends Bas @Entity(name = "ProductNaturalId") public class ProductNaturalId { - @Id - private String naturalId; - @OneToOne(optional = false) - private Product product; - } + @Id + private String naturalId; + @OneToOne(optional = false) + private Product product; + } @Entity(name = "Product") public class Product { - @Id - private Long id; - @OneToOne(mappedBy = "product", cascade = ALL, fetch = FetchType.LAZY) - private ProductDetail productDetail; - @OneToOne(mappedBy = "product", cascade = ALL, fetch = FetchType.LAZY) - private ProductNaturalId naturalId; - @ElementCollection(fetch = FetchType.LAZY) - private Map normalizedPricesByUnit = new HashMap<>(); - } + @Id + private Long id; + @OneToOne(mappedBy = "product", cascade = ALL, fetch = FetchType.LAZY) + private ProductDetail productDetail; + @OneToOne(mappedBy = "product", cascade = ALL, fetch = FetchType.LAZY) + private ProductNaturalId naturalId; + @ElementCollection(fetch = FetchType.LAZY) + private Map normalizedPricesByUnit = new HashMap<>(); + } - @Entity(name = "ProductDetail") - public class ProductDetail { - @Id - private Long id; - @OneToOne(optional = false) - @JoinColumn(name = "id") - @MapsId - private Product product; - } + @Entity(name = "ProductDetail") + public class ProductDetail { + @Id + private Long id; + @OneToOne(optional = false) + @JoinColumn(name = "id") + @MapsId + private Product product; + } } From afd6818e5570e1ac25603faeb785baff41c6bd20 Mon Sep 17 00:00:00 2001 From: Moritz Becker Date: Wed, 16 Jan 2019 08:55:33 +0100 Subject: [PATCH 08/13] HHH-13201 - mark local origin variable as final --- .../java/org/hibernate/hql/internal/ast/tree/SelectClause.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java index 93b8c17451..df33118d0e 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java @@ -193,7 +193,7 @@ public class SelectClause extends SelectExpressionList { FromElement fromElement = (FromElement) iterator.next(); if ( fromElement.isFetch() ) { - FromElement origin; + final FromElement origin; if ( fromElement.getRealOrigin() == null ) { // work around that crazy issue where the tree contains // "empty" FromElements (no text); afaict, this is caused From 6365204c488eb2db1dbe52261a59a726e0fb425c Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Fri, 28 Aug 2020 16:42:40 -0400 Subject: [PATCH 09/13] HHH-13058 fix issue left join root cannot be replaced by correlated parent in subquery --- .../criteria/internal/FromImplementor.java | 3 + .../criteria/internal/QueryStructure.java | 73 ++++++++-- .../internal/path/AbstractFromImpl.java | 22 ++- .../internal/hhh13058/HHH13058Test.java | 133 ++++++++++++++++++ .../criteria/internal/hhh13058/Patient.java | 32 +++++ .../criteria/internal/hhh13058/Site.java | 20 +++ .../criteria/internal/hhh13058/Task.java | 56 ++++++++ 7 files changed, 322 insertions(+), 17 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh13058/HHH13058Test.java create mode 100644 hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh13058/Patient.java create mode 100644 hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh13058/Site.java create mode 100644 hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh13058/Task.java diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/FromImplementor.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/FromImplementor.java index 0aefb558de..2a6265de95 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/FromImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/FromImplementor.java @@ -22,4 +22,7 @@ public interface FromImplementor extends PathImplementor, From { FromImplementor correlateTo(CriteriaSubqueryImpl subquery); void prepareCorrelationDelegate(FromImplementor parent); FromImplementor getCorrelationParent(); + default boolean canBeReplacedByCorrelatedParentInSubQuery() { + return isCorrelated(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/QueryStructure.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/QueryStructure.java index 5023de0b34..e4bddf4a54 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/QueryStructure.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/QueryStructure.java @@ -318,18 +318,33 @@ public class QueryStructure implements Serializable { final FromImplementor correlationParent = correlationRoot.getCorrelationParent(); correlationParent.prepareAlias( renderingContext ); final String correlationRootAlias = correlationParent.getAlias(); - for ( Join correlationJoin : correlationRoot.getJoins() ) { - final JoinImplementor correlationJoinImpl = (JoinImplementor) correlationJoin; - // IMPL NOTE: reuse the sep from above! + if ( correlationRoot.canBeReplacedByCorrelatedParentInSubQuery() ) { + for ( Join correlationJoin : correlationRoot.getJoins() ) { + final JoinImplementor correlationJoinImpl = (JoinImplementor) correlationJoin; + // IMPL NOTE: reuse the sep from above! + jpaqlQuery.append( sep ); + correlationJoinImpl.prepareAlias( renderingContext ); + jpaqlQuery.append( correlationRootAlias ) + .append( '.' ) + .append( correlationJoinImpl.getAttribute().getName() ) + .append( " as " ) + .append( correlationJoinImpl.getAlias() ); + sep = ", "; + renderJoins( jpaqlQuery, renderingContext, correlationJoinImpl.getJoins() ); + } + } + else { + correlationRoot.prepareAlias( renderingContext ); jpaqlQuery.append( sep ); - correlationJoinImpl.prepareAlias( renderingContext ); - jpaqlQuery.append( correlationRootAlias ) - .append( '.' ) - .append( correlationJoinImpl.getAttribute().getName() ) - .append( " as " ) - .append( correlationJoinImpl.getAlias() ); sep = ", "; - renderJoins( jpaqlQuery, renderingContext, correlationJoinImpl.getJoins() ); + jpaqlQuery.append( correlationRoot.renderTableExpression( renderingContext ) ); + renderJoins( jpaqlQuery, renderingContext, correlationRoot.getJoins() ); + if ( correlationRoot instanceof Root ) { + Set treats = ( (RootImpl) correlationRoot ).getTreats(); + for ( TreatedRoot treat : treats ) { + renderJoins( jpaqlQuery, renderingContext, treat.getJoins() ); + } + } } } } @@ -341,20 +356,48 @@ public class QueryStructure implements Serializable { } protected void renderWhereClause(StringBuilder jpaqlQuery, RenderingContext renderingContext) { - if ( getRestriction() == null ) { + final String correlationRestrictionWhereFragment = getCorrelationRestrictionsWhereFragment(); + if ( getRestriction() == null && correlationRestrictionWhereFragment.isEmpty() ) { return; } renderingContext.getClauseStack().push( Clause.WHERE ); try { - jpaqlQuery.append( " where " ) - .append( ( (Renderable) getRestriction() ).render( renderingContext ) ); + jpaqlQuery.append( " where " ); + jpaqlQuery.append( correlationRestrictionWhereFragment ); + if ( getRestriction() != null ) { + if ( !correlationRestrictionWhereFragment.isEmpty() ) { + jpaqlQuery.append( " and ( " ); + } + jpaqlQuery.append( ( (Renderable) getRestriction() ).render( renderingContext ) ); + if ( !correlationRestrictionWhereFragment.isEmpty() ) { + jpaqlQuery.append( " )" ); + } + } } finally { renderingContext.getClauseStack().pop(); } } + private String getCorrelationRestrictionsWhereFragment() { + if ( !isSubQuery || correlationRoots == null ) { + return ""; + } + StringBuilder buffer = new StringBuilder(); + String sep = ""; + for ( FromImplementor correlationRoot : correlationRoots ) { + if ( !correlationRoot.canBeReplacedByCorrelatedParentInSubQuery() ) { + buffer.append( sep ); + sep = " and "; + buffer.append( correlationRoot.getAlias() ) + .append( "=" ) + .append( correlationRoot.getCorrelationParent().getAlias() ); + } + } + return buffer.toString(); + } + protected void renderGroupByClause(StringBuilder jpaqlQuery, RenderingContext renderingContext) { if ( getGroupings().isEmpty() ) { return; @@ -395,7 +438,7 @@ public class QueryStructure implements Serializable { private void renderJoins( StringBuilder jpaqlQuery, RenderingContext renderingContext, - Collection> joins) { + Collection> joins) { if ( joins == null ) { return; } @@ -428,7 +471,7 @@ public class QueryStructure implements Serializable { private void renderFetches( StringBuilder jpaqlQuery, RenderingContext renderingContext, - Collection fetches) { + Collection fetches) { if ( fetches == null ) { return; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/path/AbstractFromImpl.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/path/AbstractFromImpl.java index ac6a702558..b78d7dcd88 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/path/AbstractFromImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/path/AbstractFromImpl.java @@ -81,7 +81,7 @@ public abstract class AbstractFromImpl @Override public void prepareAlias(RenderingContext renderingContext) { if ( getAlias() == null ) { - if ( isCorrelated() ) { + if ( canBeReplacedByCorrelatedParentInSubQuery() ) { setAlias( getCorrelationParent().getAlias() ); } else { @@ -207,7 +207,7 @@ public abstract class AbstractFromImpl @Override public String getAlias() { - return isCorrelated() ? getCorrelationParent().getAlias() : super.getAlias(); + return canBeReplacedByCorrelatedParentInSubQuery() ? getCorrelationParent().getAlias() : super.getAlias(); } // JOINS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -589,4 +589,22 @@ public abstract class AbstractFromImpl return (Fetch) fetch( (SingularAttribute) attribute, jt ); } } + + @Override + public boolean canBeReplacedByCorrelatedParentInSubQuery() { + if ( correlationParent == null ) { + return false; + } + if ( joins == null ) { + return true; + } + for ( Join join : joins ) { + // HHH-13058: substitution implies INNER JOIN + if ( join.getJoinType() == JoinType.LEFT ) { + return false; + } + assert join.getJoinType() == JoinType.INNER; + } + return true; + } } diff --git a/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh13058/HHH13058Test.java b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh13058/HHH13058Test.java new file mode 100644 index 0000000000..1961ba60c4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh13058/HHH13058Test.java @@ -0,0 +1,133 @@ +package org.hibernate.query.criteria.internal.hhh13058; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.From; +import javax.persistence.criteria.JoinType; +import javax.persistence.criteria.Root; +import javax.persistence.criteria.Subquery; + +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.testing.TestForIssue; +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertThat; + +/** + * @author Archie Cobbs + * @author Nathan Xu + */ +@TestForIssue( jiraKey = "HHH-13058" ) +public class HHH13058Test extends BaseEntityManagerFunctionalTestCase { + + private Set validSites; + + private Task taskWithoutPatient; + private Task taskWithPatientWithoutSite; + private Task taskWithPatient1WithValidSite1; + private Task taskWithPatient2WithValidSite1; + private Task taskWithPatient3WithValidSite2; + private Task taskWithPatientWithInvalidSite; + + @Override + public Class[] getAnnotatedClasses() { + return new Class[] { + Task.class, + Patient.class, + Site.class + }; + } + + @Before + public void setUp() { + doInJPA( this::entityManagerFactory, entityManager -> { + final Site validSite1 = new Site(); + final Site validSite2 = new Site(); + final Site invalidSite = new Site(); + + entityManager.persist( validSite1 ); + entityManager.persist( validSite2 ); + entityManager.persist( invalidSite ); + + validSites = new HashSet<>( Arrays.asList( validSite1, validSite2 ) ); + + final Patient patientWithoutSite = new Patient(); + final Patient patient1WithValidSite1 = new Patient( validSite1 ); + final Patient patient2WithValidSite1 = new Patient( validSite1 ); + final Patient patient3WithValidSite2 = new Patient( validSite2 ); + final Patient patientWithInvalidSite = new Patient( invalidSite ); + + entityManager.persist( patientWithoutSite ); + entityManager.persist( patient1WithValidSite1 ); + entityManager.persist( patient2WithValidSite1 ); + entityManager.persist( patient3WithValidSite2 ); + entityManager.persist( patientWithInvalidSite ); + + taskWithoutPatient = new Task(); + taskWithoutPatient.description = "taskWithoutPatient"; + + taskWithPatientWithoutSite = new Task( patientWithoutSite ); + taskWithPatientWithoutSite.description = "taskWithPatientWithoutSite"; + + taskWithPatient1WithValidSite1 = new Task( patient1WithValidSite1 ); + taskWithPatient1WithValidSite1.description = "taskWithPatient1WithValidSite1"; + + taskWithPatient2WithValidSite1 = new Task( patient2WithValidSite1 ); + taskWithPatient2WithValidSite1.description = "taskWithPatient2WithValidSite1"; + + taskWithPatient3WithValidSite2 = new Task( patient3WithValidSite2 ); + taskWithPatient3WithValidSite2.description = "taskWithPatient3WithValidSite2"; + + taskWithPatientWithInvalidSite = new Task( patientWithInvalidSite ); + taskWithPatientWithInvalidSite.description = "taskWithPatientWithInvalidSite"; + + entityManager.persist( taskWithoutPatient ); + entityManager.persist( taskWithPatientWithoutSite ); + entityManager.persist( taskWithPatient1WithValidSite1 ); + entityManager.persist( taskWithPatient2WithValidSite1 ); + entityManager.persist( taskWithPatient3WithValidSite2 ); + entityManager.persist( taskWithPatientWithInvalidSite ); + } ); + } + + @Test + public void testCorrelateSubQueryLeftJoin() { + doInJPA( this::entityManagerFactory, entityManager -> { + final CriteriaBuilder builder = entityManager.getCriteriaBuilder(); + final CriteriaQuery outerQuery = builder.createQuery( Task.class ); + final Root outerTask = outerQuery.from( Task.class ); + + final Subquery subquery = outerQuery.subquery( Task.class ); + final Root subtask = subquery.correlate( outerTask ); + final From patient = subtask.join( Task_.patient, JoinType.LEFT ); + final From site = patient.join( Patient_.site, JoinType.LEFT ); + outerQuery.where( + builder.exists( + subquery.select( subtask ) + .where( + builder.or( + patient.isNull(), + site.in( validSites ) + ) + ) + ) + ); + final List tasks = entityManager.createQuery( outerQuery ).getResultList(); + assertThat( new HashSet<>( tasks ), is( new HashSet<>( Arrays.asList( + taskWithoutPatient, + taskWithPatient1WithValidSite1, + taskWithPatient2WithValidSite1, + taskWithPatient3WithValidSite2 + ) ) ) ); + + } ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh13058/Patient.java b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh13058/Patient.java new file mode 100644 index 0000000000..a314c5908f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh13058/Patient.java @@ -0,0 +1,32 @@ +package org.hibernate.query.criteria.internal.hhh13058; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +/** + * @author Archie Cobbs + * @author Nathan Xu + */ +@Entity(name = "Patient") +@Table(name = "Patient") +public class Patient { + + @Id + @GeneratedValue + Long id; + + @ManyToOne(fetch = FetchType.LAZY) + Site site; + + public Patient() { + } + + public Patient(Site site) { + this.site = site; + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh13058/Site.java b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh13058/Site.java new file mode 100644 index 0000000000..3913ad5d6d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh13058/Site.java @@ -0,0 +1,20 @@ +package org.hibernate.query.criteria.internal.hhh13058; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * @author Archie Cobbs + * @author Nathan Xu + */ +@Entity(name = "Site") +@Table(name = "Site") +public class Site { + + @Id + @GeneratedValue + Long id; + +} diff --git a/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh13058/Task.java b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh13058/Task.java new file mode 100644 index 0000000000..48a0ec56f3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh13058/Task.java @@ -0,0 +1,56 @@ +package org.hibernate.query.criteria.internal.hhh13058; + +import java.util.Objects; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +/** + * @author Archie Cobbs + * @author Nathan Xu + */ +@Entity(name = "Task") +@Table(name = "Task") +public class Task { + + @Id + @GeneratedValue + Long id; + + @ManyToOne(fetch = FetchType.LAZY) + Patient patient; + + String description; + + public Task() { + } + + public Task(Patient patient) { + this.patient = patient; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + Task task = (Task) o; + return id.equals( task.id ); + } + + @Override + public int hashCode() { + return Objects.hash( id ); + } + + @Override + public String toString() { + return String.format( "Task(id: %d; description: %s)", id, description == null ? "null" : description ); + } +} From 17d365ecf8965044227dc401f13fec86e3117e02 Mon Sep 17 00:00:00 2001 From: Jan-Willem Gmelig Meyling Date: Sat, 27 Jun 2020 20:14:22 +0200 Subject: [PATCH 10/13] HHH-14198 - Expose CompositeUserTypes through JPA Metamodel Composite User Types work like regular Composite Types (like Embeddable) in HQL. However, because they cannot be represented in the JPA metamodel, libraries like [GraphQL for JPA](https://github.com/jcrygier/graphql-jpa) or [Blaze-Persistence](https://persistence.blazebit.com/) cannot fully utilize them. In order to make the composite property names available to these libraries, it would be nice to optionally expose these attributes as embedded attributes. This pull request aims to make that change and makes it configurable through a custom setting. Composite User Types are a common solution for mapping composite interfaces. A common example is for example `Money` from the Java Money API (JSR-354), for which composite user types are implemented in [Jadira](http://jadira.sourceforge.net/usertype-userguide.html). I know Composite User Types are currently not consiered in Hibernate 6.x. See also [this](https://hibernate.zulipchat.com/#narrow/stream/132094-hibernate-orm-dev/topic/CompositeUserType) Zulip thread. I am not sure if Hibernate 6.x will even have multi column types, which I presume would be a requirement to even introduce Composite User types back at some point. Usually Embeddables are a much easier, suitable mechanism for composite user types. But Embeddables are not always a viable alternative, because Embeddables require the type to be subclassed (as an interface cannot be mapped, and the type may not solely comprise fields that can be mapped to a simple basic type). To deal with this exact problem, `MonetaryAmounts` are still mapped as composite user type. There also have been suggestions to the JPA Spec to consider `AttributeConverters` for Embeddables for pracitcally the same purpose (which I think is going to be a mess of an implementation). See: https://github.com/eclipse-ee4j/jpa-api/issues/105 Anyways, regardless of whether this gets integrated in 5.x, I don't expect it to be integrated in 6.x unless we also reintroduce Composite User Types. I am willing to contribute Composite User Types for 6.x if people see benefit in it and think it can be done in the first place. --- .../metamodel/internal/AttributeFactory.java | 44 +++++++++++++++++-- .../domain/internal/EmbeddableTypeImpl.java | 8 ++-- .../domain/spi/EmbeddedTypeDescriptor.java | 4 +- .../test/cut/CompositeUserTypeTest.java | 28 ++++++++++++ 4 files changed, 74 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AttributeFactory.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AttributeFactory.java index bb3f6b491c..99985b18e7 100755 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AttributeFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AttributeFactory.java @@ -36,6 +36,7 @@ import org.hibernate.metamodel.model.domain.internal.MapMember; import org.hibernate.metamodel.model.domain.internal.MappedSuperclassTypeImpl; import org.hibernate.metamodel.model.domain.internal.PluralAttributeBuilder; import org.hibernate.metamodel.model.domain.internal.SingularAttributeImpl; +import org.hibernate.metamodel.model.domain.spi.ManagedTypeDescriptor.InFlightAccess; import org.hibernate.metamodel.model.domain.spi.PersistentAttributeDescriptor; import org.hibernate.metamodel.model.domain.spi.EmbeddedTypeDescriptor; import org.hibernate.metamodel.model.domain.spi.IdentifiableTypeDescriptor; @@ -46,6 +47,7 @@ import org.hibernate.property.access.internal.PropertyAccessMapImpl; import org.hibernate.property.access.spi.Getter; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.type.ComponentType; +import org.hibernate.type.CompositeType; import org.hibernate.type.EmbeddedComponentType; import org.hibernate.type.EntityType; @@ -95,11 +97,45 @@ public class AttributeFactory { return buildPluralAttribute( (PluralAttributeMetadata) attributeMetadata ); } final SingularAttributeMetadata singularAttributeMetadata = (SingularAttributeMetadata) attributeMetadata; - final SimpleTypeDescriptor metaModelType = determineSimpleType( singularAttributeMetadata.getValueContext() ); + SimpleTypeDescriptor metaModelType = determineSimpleType( singularAttributeMetadata.getValueContext() ); + Attribute.PersistentAttributeType jpaAttributeNature = attributeMetadata.getJpaAttributeNature(); + + if ( attributeContext.getPropertyMapping().getType().isComponentType() && jpaAttributeNature.equals( Attribute.PersistentAttributeType.BASIC ) ) { + CompositeType compositeType = (CompositeType) attributeContext.getPropertyMapping().getType(); + EmbeddableTypeImpl embeddableType = new EmbeddableTypeImpl<>( + attributeMetadata.getJavaType(), + ownerType, + compositeType, + context.getSessionFactory() + ); + context.registerEmbeddedableType(embeddableType); + + String[] propertyNames = compositeType.getPropertyNames(); + org.hibernate.type.Type[] subtypes = compositeType.getSubtypes(); + InFlightAccess inFlightAccess = embeddableType.getInFlightAccess(); + + for ( int i = 0; i < propertyNames.length; i++ ) { + SingularAttributeImpl nestedAttribute = new SingularAttributeImpl( + embeddableType, + propertyNames[i], + Attribute.PersistentAttributeType.BASIC, + new BasicTypeImpl(subtypes[i].getReturnedClass(), Type.PersistenceType.BASIC), + null, + false, + false, + property.isOptional() + ); + inFlightAccess.addAttribute(nestedAttribute); + } + + metaModelType = embeddableType; + jpaAttributeNature = Attribute.PersistentAttributeType.EMBEDDED; + } + return new SingularAttributeImpl( ownerType, attributeMetadata.getName(), - attributeMetadata.getJpaAttributeNature(), + jpaAttributeNature, metaModelType, attributeMetadata.getMember(), false, @@ -230,7 +266,7 @@ public class AttributeFactory { ); context.registerEmbeddedableType( embeddableType ); - final ManagedTypeDescriptor.InFlightAccess inFlightAccess = embeddableType.getInFlightAccess(); + final InFlightAccess inFlightAccess = embeddableType.getInFlightAccess(); final Iterator subProperties = component.getPropertyIterator(); while ( subProperties.hasNext() ) { final Property property = subProperties.next(); @@ -954,7 +990,7 @@ public class AttributeFactory { final EmbeddedTypeDescriptor embeddableType = (EmbeddedTypeDescriptor) attributeContext.getOwnerType(); final String attributeName = attributeContext.getPropertyMapping().getName(); - final Getter getter = embeddableType.getHibernateType() + final Getter getter = ( ( ComponentType ) embeddableType.getHibernateType() ) .getComponentTuplizer() .getGetter( embeddableType.getHibernateType().getPropertyIndex( attributeName ) ); return PropertyAccessMapImpl.GetterImpl.class.isInstance( getter ) diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddableTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddableTypeImpl.java index 100be7a13e..95efae1fbd 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddableTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddableTypeImpl.java @@ -13,7 +13,7 @@ import org.hibernate.graph.internal.SubGraphImpl; import org.hibernate.graph.spi.SubGraphImplementor; import org.hibernate.metamodel.model.domain.spi.EmbeddedTypeDescriptor; import org.hibernate.metamodel.model.domain.spi.ManagedTypeDescriptor; -import org.hibernate.type.ComponentType; +import org.hibernate.type.CompositeType; /** * Standard Hibernate implementation of JPA's {@link javax.persistence.metamodel.EmbeddableType} @@ -27,12 +27,12 @@ public class EmbeddableTypeImpl implements EmbeddedTypeDescriptor, Serializable { private final ManagedTypeDescriptor parent; - private final ComponentType hibernateType; + private final CompositeType hibernateType; public EmbeddableTypeImpl( Class javaType, ManagedTypeDescriptor parent, - ComponentType hibernateType, + CompositeType hibernateType, SessionFactoryImplementor sessionFactory) { super( javaType, null, null, sessionFactory ); this.parent = parent; @@ -48,7 +48,7 @@ public class EmbeddableTypeImpl return parent; } - public ComponentType getHibernateType() { + public CompositeType getHibernateType() { return hibernateType; } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/spi/EmbeddedTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/spi/EmbeddedTypeDescriptor.java index 3190d74358..de7af0c106 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/spi/EmbeddedTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/spi/EmbeddedTypeDescriptor.java @@ -9,7 +9,7 @@ package org.hibernate.metamodel.model.domain.spi; import javax.persistence.metamodel.EmbeddableType; import org.hibernate.metamodel.model.domain.EmbeddedDomainType; -import org.hibernate.type.ComponentType; +import org.hibernate.type.CompositeType; /** * Hibernate extension to the JPA {@link EmbeddableType} descriptor @@ -17,7 +17,7 @@ import org.hibernate.type.ComponentType; * @author Steve Ebersole */ public interface EmbeddedTypeDescriptor extends EmbeddedDomainType, ManagedTypeDescriptor { - ComponentType getHibernateType(); + CompositeType getHibernateType(); ManagedTypeDescriptor getParent(); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/cut/CompositeUserTypeTest.java b/hibernate-core/src/test/java/org/hibernate/test/cut/CompositeUserTypeTest.java index 1f02f107b7..7d8d3678d1 100755 --- a/hibernate-core/src/test/java/org/hibernate/test/cut/CompositeUserTypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/cut/CompositeUserTypeTest.java @@ -6,18 +6,24 @@ */ package org.hibernate.test.cut; +import java.lang.reflect.Member; import java.math.BigDecimal; import java.util.Currency; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; import org.hibernate.criterion.Restrictions; import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.SybaseASE15Dialect; import org.hibernate.hql.internal.ast.QuerySyntaxException; +import org.hibernate.metamodel.model.domain.spi.PersistentAttributeDescriptor; +import org.hibernate.metamodel.model.domain.spi.SingularPersistentAttribute; +import org.hibernate.metamodel.spi.MetamodelImplementor; import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.SkipForDialect; @@ -25,13 +31,20 @@ import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; +import javax.persistence.metamodel.Attribute; +import javax.persistence.metamodel.ManagedType; +import javax.persistence.metamodel.SingularAttribute; + import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; /** * @author Gavin King */ public class CompositeUserTypeTest extends BaseCoreFunctionalTestCase { + @Override public String[] getMappings() { return new String[] { "cut/types.hbm.xml", "cut/Transaction.hbm.xml" }; @@ -67,6 +80,21 @@ public class CompositeUserTypeTest extends BaseCoreFunctionalTestCase { t.commit(); s.close(); } + + @Test + public void testMetamodel() { + MetamodelImplementor metamodel = sessionFactory().getMetamodel(); + PersistentAttributeDescriptor value = metamodel.managedType(Transaction.class).getAttribute("value"); + assertEquals(Attribute.PersistentAttributeType.EMBEDDED, value.getPersistentAttributeType()); + + SingularPersistentAttribute singularPersistentAttribute = (SingularPersistentAttribute) value; + ManagedType attribute = (ManagedType) singularPersistentAttribute.getType(); + SingularAttribute amount = attribute.getSingularAttribute("amount"); + assertNotNull(amount); + + Member javaMember = amount.getJavaMember(); + assertNull(javaMember); + } @Test @SkipForDialect ( value = { SybaseASE15Dialect.class }, jiraKey = "HHH-6788" ) From 37a60ea8bb30fe3c9729a816196e3ef5fabe67e5 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Thu, 3 Sep 2020 10:26:54 -0400 Subject: [PATCH 11/13] HHH-14197 Criteria API doubly-nested subquery generates invalid SQL - missing subquery root --- .../internal/ast/tree/FromElementFactory.java | 8 +- .../internal/hhh14197/AbstractPersistent.java | 30 +++++ .../internal/hhh14197/Department.java | 32 +++++ .../criteria/internal/hhh14197/Employee.java | 118 ++++++++++++++++++ .../internal/hhh14197/HHH14197Test.java | 60 +++++++++ 5 files changed, 244 insertions(+), 4 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/AbstractPersistent.java create mode 100644 hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/Department.java create mode 100644 hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/Employee.java create mode 100644 hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/HHH14197Test.java diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementFactory.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementFactory.java index f304ff7123..c16a874bc5 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementFactory.java @@ -226,7 +226,7 @@ public class FromElementFactory implements SqlTokenTypes { } if ( explicitSubqueryFromElement ) { - // Treat explict from elements in sub-queries properly. + // Treat explicit from elements in sub-queries properly. elem.setInProjectionList( true ); } @@ -361,14 +361,14 @@ public class FromElementFactory implements SqlTokenTypes { destination.initializeCollection( fromClause, classAlias, tableAlias ); destination.setType( JOIN_FRAGMENT ); // Tag this node as a JOIN. destination.setIncludeSubclasses( false ); // Don't include subclasses in the join. - destination.setCollectionJoin( true ); // This is a clollection join. + destination.setCollectionJoin( true ); // This is a collection join. destination.setJoinSequence( collectionJoinSequence ); destination.setOrigin( origin, false ); destination.setCollectionTableAlias( tableAlias ); // origin.addDestination( destination ); // This was the cause of HHH-242 // origin.setType( FROM_FRAGMENT ); // Set the parent node type so that the AST is properly formed. - if ( origin.getQueryableCollection() != null ) { + if ( origin.getQueryableCollection() != null && !origin.inProjectionList() ) { origin.setText( "" ); // The destination node will have all the FROM text. } origin.setCollectionJoin( true ); // The parent node is a collection join too (voodoo - see JoinProcessor) @@ -546,7 +546,7 @@ public class FromElementFactory implements SqlTokenTypes { private String[] getColumns() { if ( columns == null ) { - throw new IllegalStateException( "No foriegn key columns were supplied!" ); + throw new IllegalStateException( "No foreign key columns were supplied!" ); } return columns; } diff --git a/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/AbstractPersistent.java b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/AbstractPersistent.java new file mode 100644 index 0000000000..8ab3a89042 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/AbstractPersistent.java @@ -0,0 +1,30 @@ +package org.hibernate.query.criteria.internal.hhh14197; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; + +/** + * @author Archie Cobbs + */ + +@MappedSuperclass +public abstract class AbstractPersistent { + + private long id; + + protected AbstractPersistent() { + } + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false) + public long getId() { + return this.id; + } + public void setId(long id) { + this.id = id; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/Department.java b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/Department.java new file mode 100644 index 0000000000..f3ec2eb5d6 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/Department.java @@ -0,0 +1,32 @@ +package org.hibernate.query.criteria.internal.hhh14197; + +import java.util.HashSet; +import java.util.Set; +import javax.persistence.Entity; +import javax.persistence.OneToMany; + +/** + * @author Archie Cobbs + */ + +@Entity +public class Department extends AbstractPersistent { + + private String name; + private Set employees = new HashSet<>(); + + public String getName() { + return this.name; + } + public void setName(String name) { + this.name = name; + } + + @OneToMany(mappedBy = "department") + public Set getEmployees() { + return this.employees; + } + public void setEmployees(Set employees) { + this.employees = employees; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/Employee.java b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/Employee.java new file mode 100644 index 0000000000..77c18d3105 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/Employee.java @@ -0,0 +1,118 @@ +package org.hibernate.query.criteria.internal.hhh14197; + +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.ManyToOne; +import javax.persistence.MapKeyColumn; +import javax.persistence.OneToMany; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +/** + * @author Archie Cobbs + */ + +@Entity +public class Employee extends AbstractPersistent { + + private String name; + private float salary; + private Seniority seniority; + private Date startDate; + private Department department; + private Employee manager; + private Set directReports = new HashSet<>(); + private Map annotations = new HashMap<>(); + + public String getName() { + return this.name; + } + public void setName(String name) { + this.name = name; + } + + public float getSalary() { + return this.salary; + } + public void setSalary(float salary) { + this.salary = salary; + } + + @Enumerated(EnumType.STRING) + public Seniority getSeniority() { + return this.seniority; + } + public void setSeniority(Seniority seniority) { + this.seniority = seniority; + } + + @Temporal(TemporalType.TIMESTAMP) + public Date getStartDate() { + return this.startDate; + } + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + @ManyToOne + public Employee getManager() { + return this.manager; + } + public void setManager(Employee manager) { + this.manager = manager; + } + + @ManyToOne + public Department getDepartment() { + return this.department; + } + public void setDepartment(Department department) { + this.department = department; + } + + @OneToMany(mappedBy = "manager") + public Set getDirectReports() { + return this.directReports; + } + public void setDirectReports(Set directReports) { + this.directReports = directReports; + } + + @ElementCollection + @MapKeyColumn(name = "name", length = 180) + @Column(name = "value", length = 65535, nullable = false) + public Map getAnnotations() { + return this.annotations; + } + public void setAnnotations(Map annotations) { + this.annotations = annotations; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + + "[name=" + (this.name != null ? "\"" + this.name + "\"" : null) + + ",salary=" + this.salary + + ",startDate=" + this.startDate + + ",department=" + this.department + + ",manager=" + this.manager + + ",directReports=" + this.directReports + + ",annotations=" + this.annotations + + "]"; + } + +// Seniority + + public enum Seniority { + JUNIOR, + SENIOR; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/HHH14197Test.java b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/HHH14197Test.java new file mode 100644 index 0000000000..42eb514cff --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/HHH14197Test.java @@ -0,0 +1,60 @@ +package org.hibernate.query.criteria.internal.hhh14197; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.MapJoin; +import javax.persistence.criteria.Root; +import javax.persistence.criteria.SetJoin; +import javax.persistence.criteria.Subquery; + +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.testing.FailureExpected; +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; + +/** + * @author Archie Cobbs + * @author Nathan Xu + */ +@TestForIssue( jiraKey = "HHH-14197" ) +public class HHH14197Test extends BaseEntityManagerFunctionalTestCase { + + @Override + public Class[] getAnnotatedClasses() { + return new Class[] { + Department.class, + Employee.class + }; + } + + @Test + public void testValidSQLGenerated() { + // without fixing HHH-14197, invalid SQL would be generated without root + // "... where exists (select employee0_.id as id1_1_ from where ...) ... " + doInJPA( this::entityManagerFactory, entityManager -> { + final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + final CriteriaQuery query = cb.createQuery( Employee.class ); + final Root employee = query.from( Employee.class ); + + final Subquery subquery1 = query.subquery( Employee.class ); + final Root employee2 = subquery1.correlate( employee ); + final SetJoin directReport = employee2.join( Employee_.directReports ); + + final Subquery subquery2 = subquery1.subquery( Employee.class ); + final SetJoin directReport2 = subquery2.correlate( directReport ); + directReport2.join( Employee_.annotations ); + + subquery2.select( directReport2 ); + + subquery1.select( employee2 ).where( cb.exists( subquery2 ) ); + + query.select( employee ).where( cb.exists( subquery1 ) ); + + entityManager.createQuery( query ).getResultList(); + } ); + } + +} From 6cc63d9289e87fb7e8478489855d22a59fa936d9 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 7 Sep 2020 09:30:53 +0100 Subject: [PATCH 12/13] HHH-14113 Fix oracle test failure --- .../jpa/test/graphs/EntityGraphAttributeResolutionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/graphs/EntityGraphAttributeResolutionTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/graphs/EntityGraphAttributeResolutionTest.java index 065ee60929..6e3d7fabee 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/graphs/EntityGraphAttributeResolutionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/graphs/EntityGraphAttributeResolutionTest.java @@ -183,7 +183,7 @@ public class EntityGraphAttributeResolutionTest extends BaseEntityManagerFunctio @Enumerated(EnumType.STRING) @ElementCollection(targetClass = Permission.class) - @CollectionTable(name = "USERS_PERMISSIONS", joinColumns = @JoinColumn(name = "uid")) + @CollectionTable(name = "USERS_PERMISSIONS", joinColumns = @JoinColumn(name = "user_id")) private Set permissions = EnumSet.of( Permission.FOO ); @ManyToMany(fetch = FetchType.LAZY) From 7f6ead80cce62c6b0b4ab264808a96064ea90cc8 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 7 Sep 2020 14:18:42 +0100 Subject: [PATCH 13/13] HHH-14197 Fix MariaDB failing test --- .../hibernate/query/criteria/internal/hhh14197/Employee.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/Employee.java b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/Employee.java index 77c18d3105..20ef889104 100644 --- a/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/Employee.java +++ b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/hhh14197/Employee.java @@ -88,7 +88,7 @@ public class Employee extends AbstractPersistent { @ElementCollection @MapKeyColumn(name = "name", length = 180) - @Column(name = "value", length = 65535, nullable = false) + @Column(name = "value", nullable = false) public Map getAnnotations() { return this.annotations; }