From 168a5dc469dd8df2246c449547388cb80b4f02d6 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 22 Dec 2023 10:23:25 +0100 Subject: [PATCH 001/321] Fix test on CockroachDB by not using identity generator --- .../org/hibernate/orm/test/proxy/ProxyWithGenericsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyWithGenericsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyWithGenericsTest.java index ca88a2dd25b1..2f0321eb5b3d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyWithGenericsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyWithGenericsTest.java @@ -114,7 +114,7 @@ public interface SquareEntity extends ShapeEntity { @Access(AccessType.FIELD) public static abstract class AbstractEntityImpl { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue @Column(name = "ID") Long id; } From ff591e54ed91165c82b1fee357eec7cb5265cae1 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 21 Dec 2023 11:05:44 -0600 Subject: [PATCH 002/321] Be sensitive to changes in `databases.gradle` in terms of invalidating build-cache (cherry picked from commit e97e76182bae4c7719775a9a8b86889ffdce8fc3) --- gradle/java-module.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle/java-module.gradle b/gradle/java-module.gradle index c0366105fc4f..c58fd59e132a 100644 --- a/gradle/java-module.gradle +++ b/gradle/java-module.gradle @@ -269,6 +269,7 @@ processTestResources { duplicatesStrategy DuplicatesStrategy.INCLUDE inputs.property( "db", db ) inputs.property( "dbHost", dbHost ) + inputs.file( rootProject.file( "gradle/databases.gradle" ) ) doLast { copy { from( sourceSets.test.java.srcDirs ) { From cbf4acde3317bc2dd431828890587f5d81316a2c Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 22 Dec 2023 19:34:22 +0100 Subject: [PATCH 003/321] Fix running GH workflows on 6.4 branch --- .github/workflows/atlas.yml | 4 ++-- .github/workflows/codeql.yml | 4 ++-- .github/workflows/contributor-build.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/atlas.yml b/.github/workflows/atlas.yml index 74c032515d09..e1ec067548df 100644 --- a/.github/workflows/atlas.yml +++ b/.github/workflows/atlas.yml @@ -9,10 +9,10 @@ name: Hibernate ORM build-Atlas on: push: branches: - - 'main' + - '6.4' pull_request: branches: - - 'main' + - '6.4' permissions: {} # none diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index a05c7b585b5d..b9027b7cc88f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,10 +2,10 @@ name: "CodeQL" on: push: - branches: [ 'main' ] + branches: [ '6.4' ] pull_request: # The branches below must be a subset of the branches above - branches: [ 'main' ] + branches: [ '6.4' ] schedule: - cron: '34 11 * * 4' diff --git a/.github/workflows/contributor-build.yml b/.github/workflows/contributor-build.yml index 85fb68309020..fbaad2fa68a3 100644 --- a/.github/workflows/contributor-build.yml +++ b/.github/workflows/contributor-build.yml @@ -9,10 +9,10 @@ name: Hibernate ORM build on: push: branches: - - 'main' + - '6.4' pull_request: branches: - - 'main' + - '6.4' permissions: {} # none From 2521e72d372b0bf05108defca461ecb21f553568 Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Thu, 21 Dec 2023 23:23:47 +0100 Subject: [PATCH 004/321] HHH-14358 - Added test and fix to support null binding for PostgreSQL Signed-off-by: Jan Schatteman --- .../dialect/CockroachLegacyDialect.java | 5 +- .../dialect/PostgreSQLLegacyDialect.java | 8 +-- .../hibernate/dialect/CockroachDialect.java | 2 +- .../hibernate/dialect/PostgreSQLDialect.java | 3 +- .../dialect/PostgreSQLUUIDJdbcType.java | 57 +++++++++++++++++++ ...ValueTest.java => PostgreSQLUUIDTest.java} | 31 +++++++++- .../test/type/PreferredUuidJdbcTypeTest.java | 4 +- 7 files changed, 95 insertions(+), 15 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLUUIDJdbcType.java rename hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/{PostgreSQLUUIDGeneratedValueTest.java => PostgreSQLUUIDTest.java} (70%) diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java index 80c8d2da518d..e6045033cbf7 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java @@ -63,7 +63,6 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor; import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType; -import org.hibernate.type.descriptor.jdbc.UUIDJdbcType; import org.hibernate.type.descriptor.jdbc.VarbinaryJdbcType; import org.hibernate.type.descriptor.jdbc.VarcharJdbcType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; @@ -352,7 +351,7 @@ protected void contributeCockroachTypes(TypeContributions typeContributions, Ser // Don't use this type due to https://github.com/pgjdbc/pgjdbc/issues/2862 //jdbcTypeRegistry.addDescriptor( TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE ); if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) { - jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLUUIDJdbcType.INSTANCE ); if ( PgJdbcHelper.isUsable( serviceRegistry ) ) { jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getIntervalJdbcType( serviceRegistry ) ); @@ -376,7 +375,7 @@ protected void contributeCockroachTypes(TypeContributions typeContributions, Ser } } else { - jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLUUIDJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingIntervalSecondJdbcType.INSTANCE ); if ( getVersion().isSameOrAfter( 20, 0 ) ) { jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingInetJdbcType.INSTANCE ); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java index d36df7fafa5e..502de8521c4b 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java @@ -42,7 +42,6 @@ import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.dialect.unique.CreateTableUniqueDelegate; import org.hibernate.dialect.unique.UniqueDelegate; -import org.hibernate.engine.jdbc.Size; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; @@ -83,7 +82,6 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor; import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType; -import org.hibernate.type.descriptor.jdbc.UUIDJdbcType; import org.hibernate.type.descriptor.jdbc.XmlJdbcType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.type.descriptor.sql.internal.ArrayDdlTypeImpl; @@ -1365,8 +1363,8 @@ protected void contributePostgreSQLTypes(TypeContributions typeContributions, Se } if ( getVersion().isSameOrAfter( 8, 2 ) ) { - // HHH-9562 - jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE ); + // HHH-9562 / HHH-14358 + jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLUUIDJdbcType.INSTANCE ); if ( getVersion().isSameOrAfter( 9, 2 ) ) { if ( getVersion().isSameOrAfter( 9, 4 ) ) { if ( PgJdbcHelper.isUsable( serviceRegistry ) ) { @@ -1393,7 +1391,7 @@ protected void contributePostgreSQLTypes(TypeContributions typeContributions, Se jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLStructCastingJdbcType.INSTANCE ); if ( getVersion().isSameOrAfter( 8, 2 ) ) { - jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLUUIDJdbcType.INSTANCE ); if ( getVersion().isSameOrAfter( 9, 2 ) ) { if ( getVersion().isSameOrAfter( 9, 4 ) ) { jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java index b3aaa646b221..76490fb2bb92 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -369,7 +369,7 @@ protected void contributeCockroachTypes(TypeContributions typeContributions, Ser // Don't use this type due to https://github.com/pgjdbc/pgjdbc/issues/2862 //jdbcTypeRegistry.addDescriptor( TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE ); if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) { - jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLUUIDJdbcType.INSTANCE ); if ( PgJdbcHelper.isUsable( serviceRegistry ) ) { jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getIntervalJdbcType( serviceRegistry ) ); jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getInetJdbcType( serviceRegistry ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index 66f54e1fde25..7cabad34affc 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -84,7 +84,6 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor; import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType; -import org.hibernate.type.descriptor.jdbc.UUIDJdbcType; import org.hibernate.type.descriptor.jdbc.XmlJdbcType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.type.descriptor.sql.internal.ArrayDdlTypeImpl; @@ -1413,7 +1412,6 @@ protected void contributePostgreSQLTypes(TypeContributions typeContributions, Se //jdbcTypeRegistry.addDescriptor( TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptor( XmlJdbcType.INSTANCE ); - jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE ); // HHH-9562 if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) { if ( PgJdbcHelper.isUsable( serviceRegistry ) ) { jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getInetJdbcType( serviceRegistry ) ); @@ -1449,6 +1447,7 @@ protected void contributePostgreSQLTypes(TypeContributions typeContributions, Se ); jdbcTypeRegistry.addDescriptor( new PostgreSQLEnumJdbcType() ); + jdbcTypeRegistry.addDescriptor( PostgreSQLUUIDJdbcType.INSTANCE ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLUUIDJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLUUIDJdbcType.java new file mode 100644 index 000000000000..3f2c16111f14 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLUUIDJdbcType.java @@ -0,0 +1,57 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.dialect; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; +import java.util.UUID; + +import org.hibernate.type.descriptor.ValueBinder; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.jdbc.BasicBinder; +import org.hibernate.type.descriptor.jdbc.UUIDJdbcType; + +/** + * @author Jan Schatteman + */ +public class PostgreSQLUUIDJdbcType extends UUIDJdbcType { + + /** + * Singleton access + */ + public static final PostgreSQLUUIDJdbcType INSTANCE = new PostgreSQLUUIDJdbcType(); + + @Override + public ValueBinder getBinder(JavaType javaType) { + return new BasicBinder<>( javaType, this ) { + @Override + protected void doBindNull(PreparedStatement st, int index, WrapperOptions options) throws SQLException { + st.setNull( index, getJdbcType().getJdbcTypeCode(), "uuid" ); + } + + @Override + protected void doBindNull(CallableStatement st, String name, WrapperOptions options) throws SQLException { + st.setNull( name, getJdbcType().getJdbcTypeCode(), "uuid" ); + } + + @Override + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) + throws SQLException { + st.setObject( index, getJavaType().unwrap( value, UUID.class, options ) ); + } + + @Override + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) + throws SQLException { + st.setObject( name, getJavaType().unwrap( value, UUID.class, options ) ); + } + }; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/PostgreSQLUUIDGeneratedValueTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/PostgreSQLUUIDTest.java similarity index 70% rename from hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/PostgreSQLUUIDGeneratedValueTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/PostgreSQLUUIDTest.java index 893bd861bc7d..f7392fe1eaa8 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/PostgreSQLUUIDGeneratedValueTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/PostgreSQLUUIDTest.java @@ -11,15 +11,19 @@ import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; +import jakarta.persistence.Query; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.query.NativeQuery; import org.hibernate.type.StandardBasicTypes; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -31,9 +35,9 @@ * @author Vlad Mihalcea */ @RequiresDialect(value = PostgreSQLDialect.class) -@DomainModel(annotatedClasses = { PostgreSQLUUIDGeneratedValueTest.Book.class }) +@DomainModel(annotatedClasses = { PostgreSQLUUIDTest.Book.class }) @SessionFactory -public class PostgreSQLUUIDGeneratedValueTest { +public class PostgreSQLUUIDTest { private UUID id; @@ -49,6 +53,15 @@ void setUp(SessionFactoryScope scope) { assertThat( id, notNullValue() ); } + @AfterEach + void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createMutationQuery( "delete from Book" ).executeUpdate(); + } + ); + } + @Test public void testJPQL(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -77,6 +90,20 @@ public void testNativeSQL(SessionFactoryScope scope) { } ); } + @Test + @JiraKey( value = "HHH-14358" ) + public void testUUIDNullBinding(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Query query = session.createQuery( "SELECT b FROM Book b WHERE :id is null or b.id = :id", Book.class ); + query.setParameter("id", null); + List results = Assertions.assertDoesNotThrow( query::getResultList, + "Should not throw a PSQLException of type \"could not determine data type of parameter\" " ); + Assertions.assertEquals( 1, results.size() ); + } + ); + } + @Entity(name = "Book") static class Book { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/type/PreferredUuidJdbcTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/PreferredUuidJdbcTypeTest.java index 192a50705ea2..610bf9e2167e 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/type/PreferredUuidJdbcTypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/PreferredUuidJdbcTypeTest.java @@ -4,9 +4,9 @@ import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.dialect.PostgreSQLUUIDJdbcType; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.metamodel.spi.MappingMetamodelImplementor; import org.hibernate.persister.entity.EntityPersister; @@ -55,7 +55,7 @@ public void verifyMappingsUUid(SessionFactoryScope scope) { assertThat( uuidJdbcType ).isEqualTo( CharJdbcType.INSTANCE ); final JdbcType uuidType = jdbcTypeRegistry.getDescriptor( SqlTypes.UUID ); - assertThat( uuidType ).isOfAnyClassIn( UUIDJdbcType.class ); + assertThat( uuidType ).isOfAnyClassIn( UUIDJdbcType.class, PostgreSQLUUIDJdbcType.class ); // a simple duration field with no overrides - so should be using a default JdbcType assertThat( entityDescriptor.findAttributeMapping( "uuid" ) From 6768e14f8ca9dd678f81fb12e8bfbfe024d7139f Mon Sep 17 00:00:00 2001 From: "H.Lo" Date: Mon, 25 Dec 2023 15:39:26 +0100 Subject: [PATCH 005/321] HHH-17507 HHH-17574 Fixed wrap() & unwrap() & updated unit tests --- .../descriptor/java/CurrencyJavaType.java | 6 + .../type/descriptor/java/YearJavaType.java | 4 + .../mapping/basic/CurrencyMappingTests.java | 163 ++++++++++++++++-- .../mapping/type/java/YearMappingTests.java | 118 ++++++++++--- 4 files changed, 254 insertions(+), 37 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CurrencyJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CurrencyJavaType.java index 50ea82344e8a..87b3606d7224 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CurrencyJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CurrencyJavaType.java @@ -39,6 +39,9 @@ public X unwrap(Currency value, Class type, WrapperOptions options) { if ( value == null ) { return null; } + if ( Currency.class.isAssignableFrom( type ) ) { + return (X) value; + } if ( String.class.isAssignableFrom( type ) ) { return (X) value.getCurrencyCode(); } @@ -50,6 +53,9 @@ public Currency wrap(X value, WrapperOptions options) { if ( value == null ) { return null; } + if ( value instanceof Currency ) { + return (Currency) value; + } if (value instanceof String) { return Currency.getInstance( (String) value ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/YearJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/YearJavaType.java index d1a4478876bb..99dd517cb207 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/YearJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/YearJavaType.java @@ -72,6 +72,10 @@ public Year wrap(X value, WrapperOptions options) { return null; } + if ( value instanceof Year) { + return (Year) value; + } + if ( value instanceof Number ) { return Year.of( ( (Number) value ).intValue() ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/CurrencyMappingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/CurrencyMappingTests.java index 6aed0cd0cb27..a1b1cc6e2959 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/CurrencyMappingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/CurrencyMappingTests.java @@ -6,23 +6,39 @@ */ package org.hibernate.orm.test.mapping.basic; -import java.sql.Types; -import java.util.Currency; +import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Table; - +import org.assertj.core.api.Assertions; +import org.hibernate.HibernateException; +import org.hibernate.engine.jdbc.LobCreator; +import org.hibernate.engine.jdbc.NonContextualLobCreator; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; +import org.hibernate.metamodel.mapping.internal.BasicValuedCollectionPart; import org.hibernate.metamodel.spi.MappingMetamodelImplementor; +import org.hibernate.orm.test.mapping.type.java.YearMappingTests; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; - import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.CurrencyJavaType; +import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.junit.jupiter.api.Test; +import java.sql.Types; +import java.time.Year; +import java.util.Currency; +import java.util.HashSet; +import java.util.Set; +import java.util.TimeZone; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @@ -31,6 +47,7 @@ */ @DomainModel(annotatedClasses = CurrencyMappingTests.EntityWithCurrency.class) @SessionFactory +@JiraKey("HHH-17574") public class CurrencyMappingTests { @Test @@ -46,17 +63,129 @@ public void verifyMappings(SessionFactoryScope scope) { assertThat(jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo(Currency.class)); assertThat( jdbcMapping.getJdbcType(), equalTo( jdbcRegistry.getDescriptor( Types.VARCHAR))); + final EntityWithCurrency entity = createEntityWithCurrency(); + scope.inTransaction( - (session) -> { - session.persist(new EntityWithCurrency(1, Currency.getInstance("USD"))); - } + (session) -> session.persist(entity) ); scope.inTransaction( - (session) -> session.find(EntityWithCurrency.class, 1) + (session) -> session.find(EntityWithCurrency.class, 1) ); } + @Test + public void basicAssertions(final SessionFactoryScope scope) { + final SessionFactoryImplementor sessionFactory = scope.getSessionFactory(); + final JdbcTypeRegistry jdbcTypeRegistry = sessionFactory.getTypeConfiguration().getJdbcTypeRegistry(); + final EntityPersister entityDescriptor = sessionFactory.getMappingMetamodel().getEntityDescriptor( + EntityWithCurrency.class ); + { + final BasicAttributeMapping currencyAttribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( + "currency"); + Assertions.assertThat(currencyAttribute.getJdbcMapping().getJdbcType()) + .isEqualTo(jdbcTypeRegistry.getDescriptor(Types.VARCHAR)); + Assertions.assertThat(currencyAttribute.getJdbcMapping().getJavaTypeDescriptor().getJavaTypeClass()).isEqualTo( + Currency.class ); + } + { + final PluralAttributeMapping currenciesAttribute = (PluralAttributeMapping) entityDescriptor.findAttributeMapping( + "currencies" ); + final BasicValuedCollectionPart elementDescriptor = (BasicValuedCollectionPart) currenciesAttribute.getElementDescriptor(); + Assertions.assertThat( elementDescriptor.getJdbcMapping().getJdbcType() ) + .isEqualTo( jdbcTypeRegistry.getDescriptor( Types.VARCHAR ) ); + Assertions.assertThat( elementDescriptor.getJdbcMapping().getJavaTypeDescriptor().getJavaTypeClass() ).isEqualTo( + Currency.class ); + } + } + + @Test + public void testUnwrapPass() { + final CurrencyJavaType currencyJavaType = new CurrencyJavaType(); + final Currency currency = Currency.getInstance("CHF"); + { + final Currency c = currencyJavaType.unwrap(currency, Currency.class, null); + Assertions.assertThat( c ).isEqualTo( currency ); + } + { + final String c = currencyJavaType.unwrap(currency, String.class, null); + Assertions.assertThat( c ).isEqualTo( "CHF" ); + } + } + + @Test + public void testUnwrapFail() { + final CurrencyJavaType currencyJavaType = new CurrencyJavaType(); + final Currency currency = Currency.getInstance("CHF"); + { + Assertions.assertThatThrownBy( () -> + currencyJavaType.unwrap(currency, Boolean.class, null) + ).isInstanceOf( HibernateException.class ); + } + } + + @Test + public void testWrapPass() { + final CurrencyJavaType currencyJavaType = new CurrencyJavaType(); + { + final Currency usingNull = currencyJavaType.wrap(null, null); + Assertions.assertThat(usingNull).isNull(); + } + { + final Currency usingString = currencyJavaType.wrap("CHF", null); + Assertions.assertThat(usingString).isNotNull(); + } + { + final Currency usingCurrency = currencyJavaType.wrap(Currency.getInstance("CHF"), null); + Assertions.assertThat(usingCurrency).isNotNull(); + } + } + + @Test + public void testWrapFail() { + final CurrencyJavaType currencyJavaType = new CurrencyJavaType(); + { + final String usingEmptyString = ""; + Assertions.assertThatThrownBy(() -> + currencyJavaType.wrap(usingEmptyString, null) + ).isInstanceOf(IllegalArgumentException.class); + } + { + final Integer usingInteger = Integer.valueOf(269); + Assertions.assertThatThrownBy(() -> + currencyJavaType.wrap(usingInteger, null) + ).isInstanceOf(HibernateException.class); + } + { + final CurrencyJavaType usingSelf = new CurrencyJavaType(); + Assertions.assertThatThrownBy(() -> + currencyJavaType.wrap(usingSelf, null) + ).isInstanceOf(HibernateException.class); + } + } + + @Test + public void testUsage(final SessionFactoryScope scope) { + final EntityWithCurrency entity = createEntityWithCurrency(); + scope.inTransaction((session) -> session.persist(entity)); + try { + scope.inTransaction(session -> session.createQuery("from EntityWithCurrency", EntityWithCurrency.class).list()); + } + finally { + scope.inTransaction( session -> session.remove( entity ) ); + } + } + + private static EntityWithCurrency createEntityWithCurrency() { + final Currency currency = Currency.getInstance("USD"); + + final Set currencies = new HashSet<>(); + currencies.add(Currency.getInstance("CHF")); + currencies.add(Currency.getInstance("EUR")); + + return new EntityWithCurrency( 1, currency, currencies ); + } + @Entity(name = "EntityWithCurrency") @Table(name = "EntityWithCurrency") public static class EntityWithCurrency { @@ -68,12 +197,18 @@ public static class EntityWithCurrency { private Currency currency; //end::basic-Currency-example[] - public EntityWithCurrency() { - } + @ElementCollection + private Set currencies; - public EntityWithCurrency(Integer id, Currency currency) { - this.id = id; - this.currency = currency; + public EntityWithCurrency() { + // } + + public EntityWithCurrency(final Integer id, final Currency currency, final Set currencies) { + this.id = id; + this.currency = currency; + this.currencies = currencies; + } } + } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/YearMappingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/YearMappingTests.java index fb751de43e20..0b86555c1567 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/YearMappingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/YearMappingTests.java @@ -6,40 +6,33 @@ */ package org.hibernate.orm.test.mapping.type.java; -import java.sql.Types; -import java.time.Year; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - +import jakarta.persistence.*; +import org.assertj.core.api.Assertions; +import org.hibernate.HibernateException; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; import org.hibernate.metamodel.mapping.internal.BasicValuedCollectionPart; import org.hibernate.persister.entity.EntityPersister; - import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; - +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.YearJavaType; import org.junit.jupiter.api.Test; -import jakarta.persistence.CollectionTable; -import jakarta.persistence.Column; -import jakarta.persistence.ElementCollection; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.MapKeyColumn; -import jakarta.persistence.Table; +import java.sql.Types; +import java.time.Year; +import java.time.format.DateTimeParseException; +import java.util.*; import static org.assertj.core.api.Assertions.assertThat; @DomainModel( annotatedClasses = YearMappingTests.YearMappingTestEntity.class ) @SessionFactory @JiraKey( "HHH-10558" ) +@JiraKey( "HHH-17507" ) public class YearMappingTests { @Test public void basicAssertions(SessionFactoryScope scope) { @@ -69,20 +62,98 @@ public void basicAssertions(SessionFactoryScope scope) { @Test public void testUsage(SessionFactoryScope scope) { - final YearMappingTestEntity entity = new YearMappingTestEntity( 1, "one", Year.now() ); + final YearMappingTestEntity entity1 = new YearMappingTestEntity( 1, "one", Year.now() ); final YearMappingTestEntity entity2 = new YearMappingTestEntity( 2, "two", Year.parse( "+10000" ) ); scope.inTransaction( (session) -> { - session.save( entity ); - session.save( entity2 ); + session.persist( entity1 ); + session.persist( entity2 ); } ); try { - scope.inTransaction( (session) -> session.createQuery( "from YearMappingTestEntity" ).list() ); + scope.inTransaction( (session) -> session.createQuery( "from YearMappingTestEntity", YearMappingTestEntity.class ).list() ); } finally { - scope.inTransaction( session -> session.delete( entity ) ); - scope.inTransaction( session -> session.delete( entity2 ) ); + scope.inTransaction( session -> session.remove( entity1 ) ); + scope.inTransaction( session -> session.remove( entity2 ) ); + } + } + @Test + public void testUnwrapPass() { + final YearJavaType yearJavaType = new YearJavaType(); + final Year year = Year.of(1943); + { + final Year y = yearJavaType.unwrap(year, Year.class, null); + Assertions.assertThat( y ).isEqualTo( year ); + } + { + final Integer y = yearJavaType.unwrap(year, Integer.class, null); + Assertions.assertThat( y ).isEqualTo( Integer.valueOf( 1943 ) ); + } + { + final Long y = yearJavaType.unwrap(year, Long.class, null); + Assertions.assertThat( y ).isEqualTo( Long.valueOf( 1943L ) ); + } + { + final String y = yearJavaType.unwrap(year, String.class, null); + Assertions.assertThat( y ).isEqualTo( "1943" ); + } + { + final Object y = yearJavaType.unwrap(year, Object.class, null); + Assertions.assertThat( y.toString() ).isEqualTo( "1943" ); + } + } + + @Test + public void testUnwrapFail() { + final YearJavaType yearJavaType = new YearJavaType(); + final Year year = Year.of(1943); + { + Assertions.assertThatThrownBy( () -> + yearJavaType.unwrap(year, Boolean.class, null) + ).isInstanceOf( HibernateException.class ); + } + } + + @Test + public void testWrapPass() { + final YearJavaType yearJavaType = new YearJavaType(); + { + final Year usingNull = yearJavaType.wrap( null, null ); + Assertions.assertThat( usingNull ).isNull(); + } + { + final Year usingNumber = yearJavaType.wrap( 1943, null ); + Assertions.assertThat( usingNumber ).isNotNull(); + } + { + final Year usingNegative = yearJavaType.wrap( -1, null ); + Assertions.assertThat( usingNegative ).isNotNull(); + } + { + final Year usingString = yearJavaType.wrap( "1943", null ); + Assertions.assertThat( usingString ).isNotNull(); + } + { + final Year usingYear = yearJavaType.wrap( Year.of( 1943), null ); + Assertions.assertThat( usingYear ).isNotNull(); + } + } + + @Test + public void testWrapFail() { + final YearJavaType yearJavaType = new YearJavaType(); + { + final String usingEmptyString = ""; + Assertions.assertThatThrownBy(() -> + yearJavaType.wrap( usingEmptyString, null ) + ).isInstanceOf(DateTimeParseException.class); + } + { + final Date usingDate = new Date(); + Assertions.assertThatThrownBy(() -> + yearJavaType.wrap( usingDate, null ) + ).isInstanceOf(HibernateException.class); } } @@ -156,4 +227,5 @@ public void setCountByYear(Map countByYear) { this.countByYear = countByYear; } } + } From 7955b1b52e9b7f5823ada32ab0fbb742c65e3a36 Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Fri, 22 Dec 2023 22:16:42 +0100 Subject: [PATCH 006/321] HHH-17511 - Add test for issue Signed-off-by: Jan Schatteman --- .../orm/test/softdelete/HHH17511Test.java | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/softdelete/HHH17511Test.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/softdelete/HHH17511Test.java b/hibernate-core/src/test/java/org/hibernate/orm/test/softdelete/HHH17511Test.java new file mode 100644 index 000000000000..c5a90fdfbc2a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/softdelete/HHH17511Test.java @@ -0,0 +1,215 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.softdelete; + +import org.hibernate.annotations.SoftDelete; +import org.hibernate.query.Query; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +/** + * @author Jan Schatteman + */ +@JiraKey( "HHH-17511" ) +@DomainModel( + annotatedClasses = { + HHH17511Test.AUser.class, HHH17511Test.Organization.class, HHH17511Test.OrganizationMember.class + } +) +@SessionFactory +public class HHH17511Test { + + @BeforeEach + void setup(SessionFactoryScope scope) { + Long toBeDeletedOrganizationId = scope.fromTransaction( + session -> { + AUser u1 = new AUser(); + u1.setName( "John" ); + AUser u2 = new AUser(); + u2.setName( "Joe" ); + session.persist( u1 ); + session.persist( u2 ); + + Organization o1 = new Organization(); + o1.setName( "Acme" ); + Organization o2 = new Organization(); + o2.setName( "Emca" ); + session.persist( o1 ); + session.persist( o2 ); + + OrganizationMember om1 = new OrganizationMember(); + om1.setPrimary( Boolean.TRUE ); + om1.setOrganizationId( o1.getId() ); + om1.setUserId( u1.getId() ); + OrganizationMember om2 = new OrganizationMember(); + om2.setPrimary( Boolean.FALSE ); + om2.setOrganizationId( o2.getId() ); + om2.setUserId( u2.getId() ); + session.persist( om1 ); + session.persist( om2 ); + + return o2.getId(); + } + ); + + scope.inTransaction( + session -> { + Organization o = session.find( Organization.class, toBeDeletedOrganizationId ); + session.remove( o ); + } + ); + } + + @AfterEach + void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createNativeQuery( "delete from organization_member", Void.class ).executeUpdate(); + session.createNativeQuery( "delete from organization", Void.class ).executeUpdate(); + session.createNativeQuery( "delete from theusers", Void.class).executeUpdate(); + } + ); + } + + @Test + public void testSoftDeleteConditionOnJoinedEntity(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Query query = session.createQuery( "FROM OrganizationMember om INNER JOIN Organization o ON o.id = om.organizationId WHERE om.userId =:userId", OrganizationMember.class); + query.setTupleTransformer( (tuple, aliases) -> (OrganizationMember) tuple[0] ); + + query.setParameter("userId", 1L); + Assertions.assertEquals(1, query.getResultList().size() ); + + // Organization 2 has been soft-deleted so this should not give any results + query.setParameter("userId", 2L); + Assertions.assertEquals(0, query.getResultList().size() ); + } + ); + } + + @Entity(name = "Organization") + @Table(name = "organization") + @SoftDelete + public static class Organization { + + @Id + @GeneratedValue + private Long id; + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + @Entity(name = "OrganizationMember") + @Table(name = "organization_member") + @SoftDelete + public static class OrganizationMember { + @Id + @GeneratedValue + private Long id; + + @Column(name = "user_id") + private Long userId; + + @Column(name = "organization_id") + private Long organizationId; + + @Column(name = "primary_") + private Boolean primary; + + public Boolean getPrimary() { + return primary; + } + + public void setPrimary(Boolean primary) { + this.primary = primary; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getOrganizationId() { + return organizationId; + } + + public void setOrganizationId(Long organizationId) { + this.organizationId = organizationId; + } + } + + @Entity(name = "AUser") + @Table(name = "theusers") + @SoftDelete + public static class AUser { + + @Id + @GeneratedValue + private Long id; + + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + +} From 718f26022e5163eee2c6f097c24dc851159f0e03 Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Fri, 22 Dec 2023 23:07:05 +0100 Subject: [PATCH 007/321] HHH-17511 - Fix for missing condition in join with a @SoftDelete marked Entity Signed-off-by: Jan Schatteman --- .../sqm/sql/BaseSqmToSqlAstConverter.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 297cf2e124de..9a2ca086f34c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -12,6 +12,7 @@ import org.hibernate.HibernateException; import org.hibernate.Internal; import org.hibernate.LockMode; +import org.hibernate.boot.model.internal.SoftDeleteHelper; import org.hibernate.boot.model.process.internal.InferredBasicValueResolver; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.DmlTargetColumnQualifierSupport; @@ -66,6 +67,7 @@ import org.hibernate.metamodel.mapping.Restrictable; import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMappings; +import org.hibernate.metamodel.mapping.SoftDeleteMapping; import org.hibernate.metamodel.mapping.SqlExpressible; import org.hibernate.metamodel.mapping.SqlTypedMapping; import org.hibernate.metamodel.mapping.ValueMapping; @@ -3456,6 +3458,24 @@ private TableGroup consumeEntityJoin(SqmEntityJoin sqmJoin, TableGroup lhsTab ); lhsTableGroup.addTableGroupJoin( tableGroupJoin ); + entityDescriptor.applyBaseRestrictions( + tableGroupJoin::applyPredicate, + tableGroup, + true, + getLoadQueryInfluencers().getEnabledFilters(), + null, + this + ); + + final SoftDeleteMapping softDeleteMapping = entityDescriptor.getSoftDeleteMapping(); + if ( softDeleteMapping != null ) { + final Predicate softDeleteRestriction = SoftDeleteHelper.createNonSoftDeletedRestriction( + tableGroup.resolveTableReference( entityDescriptor.getSoftDeleteTableDetails().getTableName() ), + softDeleteMapping + ); + tableGroupJoin.applyPredicate( softDeleteRestriction ); + } + if ( sqmJoin.getJoinPredicate() != null ) { final SqmJoin oldJoin = currentlyProcessingJoin; currentlyProcessingJoin = sqmJoin; From f72bea49cb9acd341a051b23de11db955fd43db9 Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Wed, 3 Jan 2024 23:26:38 +0100 Subject: [PATCH 008/321] HHH-14821 - Test and fix for issue Signed-off-by: Jan Schatteman --- .../internal/DefaultRefreshEventListener.java | 4 +++ .../orm/test/refresh/RefreshTest.java | 32 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java index f4a14baa44dd..ac260791fb12 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java @@ -92,6 +92,10 @@ private static void refresh(RefreshEvent event, RefreshContext refreshedAlready, //refresh() does not pass an entityName persister = source.getEntityPersister( event.getEntityName(), object ); id = persister.getIdentifier( object, event.getSession() ); + if ( id == null ) { + throw new HibernateException( "attempted to refresh an instance that is not part of the persistence context yet: " + + infoString( persister, id, source.getFactory() )); + } if ( LOG.isTraceEnabled() ) { LOG.tracev( "Refreshing transient {0}", diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/refresh/RefreshTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/refresh/RefreshTest.java index cdc9265681c0..0dd05697db15 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/refresh/RefreshTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/refresh/RefreshTest.java @@ -6,9 +6,12 @@ import java.util.LinkedList; import java.util.Set; +import org.hibernate.HibernateException; + import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import jakarta.persistence.CascadeType; @@ -27,6 +30,7 @@ RefreshTest.RealmEntity.class, RefreshTest.RealmAttributeEntity.class, RefreshTest.ComponentEntity.class, + RefreshTest.SimpleEntity.class } ) @SessionFactory @@ -74,6 +78,34 @@ public void testIt(SessionFactoryScope scope) { ); } + @Test + public void testRefreshWithNullId(SessionFactoryScope scope) { + Assertions.assertThrows( + HibernateException.class, + () -> { + scope.inTransaction( + session -> { + SimpleEntity se = new SimpleEntity(); + se.setName( "a" ); + session.refresh( se ); + } + ); + }, + "attempted to refresh an instance that is not part of the persistence context yet: [org.hibernate.orm.test.refresh.RefreshTest$SimpleEntity#]" + ); + } + + @Entity(name= "SimpleEntity" ) + public static class SimpleEntity { + @Id + Long id; + String name; + + public void setName(String name) { + this.name = name; + } + } + @Table(name="REALM") @Entity(name = "RealmEntity") public static class RealmEntity { From f0b2eb9d1ce329ea0a8ac96ed5bf33a795ad90b8 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 3 Jan 2024 10:02:36 +0100 Subject: [PATCH 009/321] HHH-17530 Add test for issue --- ...ithDynamicInstantiationAndOrderByTest.java | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/CriteriaWithDynamicInstantiationAndOrderByTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/CriteriaWithDynamicInstantiationAndOrderByTest.java index e15797d64619..e61ab5607ff4 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/CriteriaWithDynamicInstantiationAndOrderByTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/CriteriaWithDynamicInstantiationAndOrderByTest.java @@ -3,6 +3,7 @@ import java.util.List; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.Jpa; import org.junit.jupiter.api.BeforeAll; @@ -153,6 +154,96 @@ public void test3SelectParamsOrder(EntityManagerFactoryScope scope) { ); } + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-17530" ) + public void testNestedConstruct(EntityManagerFactoryScope scope) { + scope.inTransaction( em -> { + final CriteriaBuilder cb = em.getCriteriaBuilder(); + final CriteriaQuery cq = cb.createQuery( R1.class ); + final Root root = cq.from( Item.class ); + final CompoundSelection construct = cb.construct( + R1.class, + root.get( "id" ), + cb.construct( + R2.class, + root.get( "id" ), + root.get( "name" ) + ) + ); + cq.select( construct ); + cq.orderBy( cb.asc( root.get( "id" ) ) ); + final R1 result = em.createQuery( cq ).getSingleResult(); + assertThat( result.getA() ).isEqualTo( ITEM_ID ); + assertThat( result.getR2().getA() ).isEqualTo( ITEM_ID ); + assertThat( result.getR2().getB() ).isEqualTo( ITEM_NAME ); + } ); + } + + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-17530" ) + public void testMultiselectAndNestedConstruct(EntityManagerFactoryScope scope) { + scope.inTransaction( em -> { + final CriteriaBuilder cb = em.getCriteriaBuilder(); + final CriteriaQuery cq = cb.createQuery( Tuple.class ); + final Root root = cq.from( Item.class ); + final CompoundSelection construct = cb.construct( + R1.class, + root.get( "id" ), + cb.construct( + R2.class, + root.get( "id" ), + root.get( "name" ) + ) + ); + cq.multiselect( + cb.construct( R1.class, root.get( "id" ) ), + construct + ); + cq.orderBy( cb.asc( root.get( "id" ) ) ); + final Tuple result = em.createQuery( cq ).getSingleResult(); + final R1 first = result.get( 0, R1.class ); + assertThat( first.getA() ).isEqualTo( ITEM_ID ); + final R1 second = result.get( 1, R1.class ); + assertThat( second.getA() ).isEqualTo( ITEM_ID ); + assertThat( second.getR2().getA() ).isEqualTo( ITEM_ID ); + assertThat( second.getR2().getB() ).isEqualTo( ITEM_NAME ); + } ); + } + + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-17530" ) + public void testDoubleNestedConstruct(EntityManagerFactoryScope scope) { + scope.inTransaction( em -> { + final CriteriaBuilder cb = em.getCriteriaBuilder(); + final CriteriaQuery cq = cb.createQuery( R1.class ); + final Root root = cq.from( Item.class ); + final CompoundSelection construct = cb.construct( + R1.class, + root.get( "id" ), + cb.construct( + R2.class, + root.get( "id" ), + root.get( "name" ), + cb.construct( + R3.class, + root.get( "id" ), + root.get( "name" ), + root.get( "name" ) + ) + ) + ); + cq.select( construct ); + cq.orderBy( cb.asc( root.get( "id" ) ) ); + final R1 result = em.createQuery( cq ).getSingleResult(); + assertThat( result.getA() ).isEqualTo( ITEM_ID ); + assertThat( result.getR2().getA() ).isEqualTo( ITEM_ID ); + assertThat( result.getR2().getB() ).isEqualTo( ITEM_NAME ); + assertThat( result.getR2().getR3().getA() ).isEqualTo( ITEM_ID ); + assertThat( result.getR2().getR3().getB() ).isEqualTo( ITEM_NAME ); + assertThat( result.getR2().getR3().getC() ).isEqualTo( ITEM_NAME ); + } ); + } + @Entity(name = "Item") @Table(name = "ITEM_TABLE") public static class Item { @@ -174,24 +265,43 @@ public Item(Long id, String name) { public static class R1 { private Long a; + private R2 r2; + public R1(Long a) { this.a = a; } + public R1(Long a, R2 r2) { + this.a = a; + this.r2 = r2; + } + public Long getA() { return a; } + + public R2 getR2() { + return r2; + } } public static class R2 { private Long a; private String b; + private R3 r3; + public R2(Long a, String b) { this.a = a; this.b = b; } + public R2(Long a, String b, R3 r3) { + this.a = a; + this.b = b; + this.r3 = r3; + } + public Long getA() { return a; } @@ -199,6 +309,10 @@ public Long getA() { public String getB() { return b; } + + public R3 getR3() { + return r3; + } } public static class R3 { From 8692e3d4bc37a78200db1eeacf9e7b939c1e8412 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 3 Jan 2024 10:03:00 +0100 Subject: [PATCH 010/321] HHH-17530 Handle nested dynamic instantiations when tracking selections --- .../query/sqm/sql/BaseSqmToSqlAstConverter.java | 10 ++++++---- .../internal/DynamicInstantiationArgument.java | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 9a2ca086f34c..6bf4fdedba07 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -8497,21 +8497,23 @@ private static int countIndividualSelections(List> s for ( int i = 0; i < selections.size(); i++ ) { final SqmSelectableNode selectableNode = selections.get( i ).getSelectableNode(); if ( selectableNode instanceof SqmDynamicInstantiation ) { - offset = countIndividualSelections( ( (SqmDynamicInstantiation) selectableNode ).getArguments() ) - 1; + offset += countIndividualSelections( ( (SqmDynamicInstantiation) selectableNode ).getArguments() ); } else if ( selectableNode instanceof SqmJpaCompoundSelection ) { for ( SqmSelectableNode node : ( (SqmJpaCompoundSelection) selectableNode ).getSelectionItems() ) { if ( node instanceof SqmDynamicInstantiation ) { - offset += countIndividualSelections( ( (SqmDynamicInstantiation) node ).getArguments() ) ; + offset += countIndividualSelections( ( (SqmDynamicInstantiation) node ).getArguments() ); } else { offset += 1; } } - offset -= 1; + } + else { + offset += 1; } } - return offset + selections.size(); + return offset; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationArgument.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationArgument.java index 599596ec6324..7218fa7efc38 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationArgument.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationArgument.java @@ -32,7 +32,9 @@ public ArgumentDomainResult buildArgumentDomainResult(DomainResultCreationSta .getCurrentProcessingState() .getSqlExpressionResolver(); if ( sqlExpressionResolver instanceof BaseSqmToSqlAstConverter.SqmAliasedNodeCollector ) { - ( (BaseSqmToSqlAstConverter.SqmAliasedNodeCollector) sqlExpressionResolver ).next(); + if ( !( argumentResultProducer instanceof DynamicInstantiation ) ) { + ( (BaseSqmToSqlAstConverter.SqmAliasedNodeCollector) sqlExpressionResolver ).next(); + } } return new ArgumentDomainResult<>( argumentResultProducer.createDomainResult( alias, creationState ) ); } From fa0f5d414bb32c2681eea9e690c604272b14a033 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 2 Jan 2024 09:40:04 +0100 Subject: [PATCH 011/321] HHH-17606 Add test for issue --- ...GenericMappedSuperclassNestedJoinTest.java | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericMappedSuperclassNestedJoinTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericMappedSuperclassNestedJoinTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericMappedSuperclassNestedJoinTest.java new file mode 100644 index 000000000000..63a0437bd5ff --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericMappedSuperclassNestedJoinTest.java @@ -0,0 +1,175 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.annotations.generics; + +import java.util.List; + +import org.hibernate.metamodel.model.domain.ManagedDomainType; +import org.hibernate.query.sqm.tree.domain.SqmPath; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Join; +import jakarta.persistence.criteria.Root; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Marco Belladelli + */ +@DomainModel( annotatedClasses = { + GenericMappedSuperclassNestedJoinTest.Selection.class, + GenericMappedSuperclassNestedJoinTest.SelectionProductRule.class, + GenericMappedSuperclassNestedJoinTest.SelectionProductRuleProductLink.class, + +} ) +@SessionFactory +@Jira( "https://hibernate.atlassian.net/browse/HHH-17606" ) +public class GenericMappedSuperclassNestedJoinTest { + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final Selection s1 = new Selection(); + s1.id = 1L; + s1.ident = "s1"; + session.persist( s1 ); + final SelectionProductRule s2 = new SelectionProductRule(); + s2.id = 2L; + s2.ident = 2; + s2.parent = s1; + session.persist( s2 ); + final SelectionProductRuleProductLink s3 = new SelectionProductRuleProductLink(); + s3.id = 3L; + s3.parent = s2; + session.persist( s3 ); + } ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.createMutationQuery( "delete from SelectionProductRuleProductLink" ).executeUpdate(); + session.createMutationQuery( "delete from SelectionProductRule" ).executeUpdate(); + session.createMutationQuery( "delete from Selection" ).executeUpdate(); + } ); + } + + @Test + public void testSimpleGenericJoin(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final List resultList = session.createQuery( + "select p1.ident from SelectionProductRuleProductLink s join s.parent p1", + Integer.class + ).getResultList(); + assertThat( resultList ).containsOnly( 2 ); + } ); + } + + @Test + public void testSimpleGenericJoinCriteria(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final CriteriaBuilder cb = session.getCriteriaBuilder(); + final CriteriaQuery cq = cb.createQuery( Integer.class ); + final Root root = cq.from( SelectionProductRuleProductLink.class ); + final Join parent = root.join( "parent" ); + assertThat( parent.getJavaType() ).isEqualTo( SeqOrderLinkObjectWithUserContext.class ); + assertThat( parent.getModel() ).isSameAs( root.getModel().getAttribute( "parent" ) ); + assertThat( ( (SqmPath) parent ).getResolvedModel().getBindableJavaType() ) + .isEqualTo( SelectionProductRule.class ); + final List resultList = session.createQuery( cq.select( parent.get( "ident" ) ) ).getResultList(); + assertThat( resultList ).containsOnly( 2 ); + } ); + } + + @Test + public void testNestedGenericJoin(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final List resultList = session.createQuery( + "select p2.ident from SelectionProductRuleProductLink s join s.parent p1 join p1.parent p2", + String.class + ).getResultList(); + assertThat( resultList ).containsOnly( "s1" ); + } ); + } + + @Test + public void testNestedGenericJoinCriteria(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final CriteriaBuilder cb = session.getCriteriaBuilder(); + final CriteriaQuery cq = cb.createQuery( String.class ); + final Root root = cq.from( SelectionProductRuleProductLink.class ); + final Join p1 = root.join( "parent" ); + assertThat( p1.getJavaType() ).isEqualTo( SeqOrderLinkObjectWithUserContext.class ); + assertThat( p1.getModel() ).isSameAs( root.getModel().getAttribute( "parent" ) ); + assertThat( ( (SqmPath) p1 ).getResolvedModel().getBindableJavaType() ) + .isEqualTo( SelectionProductRule.class ); + final Join p2 = p1.join( "parent" ); + assertThat( p2.getJavaType() ).isEqualTo( SimpleObject.class ); + final ManagedDomainType joinType = (ManagedDomainType) ( (SqmPath) p1 ).getReferencedPathSource() + .getSqmPathType(); + assertThat( p2.getModel() ).isSameAs( joinType.getAttribute( "parent" ) ); + assertThat( ( (SqmPath) p2 ).getResolvedModel().getBindableJavaType() ) + .isEqualTo( Selection.class ); + final List resultList = session.createQuery( cq.select( p2.get( "ident" ) ) ).getResultList(); + assertThat( resultList ).containsOnly( "s1" ); + } ); + } + + @MappedSuperclass + public static abstract class SimpleObject { + @Id + protected Long id; + } + + @MappedSuperclass + public abstract static class CommonLinkObject extends SimpleObject { + @ManyToOne + protected T parent; + } + + @MappedSuperclass + public abstract static class SeqOrderLinkObject extends CommonLinkObject { + } + + @MappedSuperclass + public abstract static class SeqOrderLinkObjectWithUserContext + extends SeqOrderLinkObject { + } + + @Entity( name = "Selection" ) + public static class Selection extends SimpleObject { + private String ident; + } + + @Entity( name = "SelectionProductRule" ) + public static class SelectionProductRule extends SeqOrderLinkObjectWithUserContext { + private Integer ident; + } + + @MappedSuperclass + public abstract static class AbsProductRuleProductLink> + extends SimpleObject { + @ManyToOne + protected T parent; + } + + @Entity( name = "SelectionProductRuleProductLink" ) + public static class SelectionProductRuleProductLink extends AbsProductRuleProductLink { + } +} \ No newline at end of file From cfe41ed8bba30dc80c69f429149f14ae788754fd Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 2 Jan 2024 11:38:49 +0100 Subject: [PATCH 012/321] HHH-17606 Fix nested generic join path resolution --- .../hql/internal/QualifiedJoinPathConsumer.java | 17 ++++------------- .../query/sqm/tree/domain/AbstractSqmPath.java | 3 +-- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java index d51e57bddbac..9f6ecb11c892 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java @@ -187,7 +187,10 @@ private AttributeJoinDelegate resolveAlias(String identifier, boolean isTerminal boolean isTerminal, boolean allowReuse, SqmCreationState creationState) { - final SqmPathSource subPathSource = subPathSource( lhs, name, creationState ); + final SqmPathSource subPathSource = lhs.getResolvedModel().getSubPathSource( + name, + creationState.getCreationContext().getJpaMetamodel() + ); if ( allowReuse && !isTerminal ) { for ( SqmJoin sqmJoin : lhs.getSqmJoins() ) { if ( sqmJoin.getAlias() == null && sqmJoin.getReferencedPathSource() == subPathSource ) { @@ -200,18 +203,6 @@ private AttributeJoinDelegate resolveAlias(String identifier, boolean isTerminal return createJoin( lhs, joinType, alias, fetch, isTerminal, allowReuse, creationState, joinSource ); } - private static SqmPathSource subPathSource(SqmFrom lhs, String name, SqmCreationState creationState) { - final SqmPathSource referencedPathSource = lhs.getReferencedPathSource(); - // We need to use referencedPathSource when it is not generic since the getResolvedModel() method would - // return the association attribute as a path source and for treated paths that might correspond to a - // different entity type (usually the first in alphabetical order) and not the correct treat target - final SqmPathSource pathSource = - referencedPathSource.isGeneric() - ? lhs.getResolvedModel() - : referencedPathSource; - return pathSource.getSubPathSource( name, creationState.getCreationContext().getJpaMetamodel() ); - } - private static SqmFrom createJoin( SqmFrom lhs, SqmJoinType joinType, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java index 6a7ca0a765c8..2fae3a9beb85 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java @@ -162,8 +162,7 @@ public SqmPathSource getModel() { public SqmPathSource getResolvedModel() { final DomainType lhsType; final SqmPathSource pathSource = getReferencedPathSource(); - if ( pathSource.isGeneric() && ( lhsType = getLhs().getReferencedPathSource() - .getSqmPathType() ) instanceof ManagedDomainType ) { + if ( pathSource.isGeneric() && ( lhsType = getLhs().getResolvedModel().getSqmPathType() ) instanceof ManagedDomainType ) { final PersistentAttribute concreteAttribute = ( (ManagedDomainType) lhsType ).findConcreteGenericAttribute( pathSource.getPathName() ); From 8e5f847201a109e4db55173347a561215f9f8e42 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 12 Dec 2023 09:58:36 +0100 Subject: [PATCH 013/321] HHH-17490 Fix not in and empty list parameter predicate --- .../dialect/DerbyLegacySqlAstTranslator.java | 2 +- .../dialect/FirebirdSqlAstTranslator.java | 2 +- .../dialect/DerbySqlAstTranslator.java | 2 +- .../sql/ast/spi/AbstractSqlAstTranslator.java | 2 +- .../orm/test/jpa/query/QueryTest.java | 50 +++++++++++++++++++ 5 files changed, 54 insertions(+), 4 deletions(-) diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacySqlAstTranslator.java index deee312b080e..26d453bbbadf 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacySqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacySqlAstTranslator.java @@ -201,7 +201,7 @@ else if ( expression instanceof Summarization ) { public void visitInListPredicate(InListPredicate inListPredicate) { final List listExpressions = inListPredicate.getListExpressions(); if ( listExpressions.isEmpty() ) { - appendSql( "1=0" ); + appendSql( "1=" + ( inListPredicate.isNegated() ? "1" : "0" ) ); return; } final Expression testExpression = inListPredicate.getTestExpression(); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdSqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdSqlAstTranslator.java index 4eaa45abe66c..8cd605ab640d 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdSqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdSqlAstTranslator.java @@ -229,7 +229,7 @@ else if ( expression instanceof Summarization ) { public void visitInListPredicate(InListPredicate inListPredicate) { final List listExpressions = inListPredicate.getListExpressions(); if ( listExpressions.isEmpty() ) { - appendSql( "1=0" ); + appendSql( "1=" + ( inListPredicate.isNegated() ? "1" : "0" ) ); return; } final Expression testExpression = inListPredicate.getTestExpression(); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DerbySqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/DerbySqlAstTranslator.java index be9f8577bb64..09df01851045 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DerbySqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DerbySqlAstTranslator.java @@ -199,7 +199,7 @@ else if ( expression instanceof Summarization ) { public void visitInListPredicate(InListPredicate inListPredicate) { final List listExpressions = inListPredicate.getListExpressions(); if ( listExpressions.isEmpty() ) { - appendSql( "1=0" ); + appendSql( "1=" + ( inListPredicate.isNegated() ? "1" : "0" ) ); return; } final Expression testExpression = inListPredicate.getTestExpression(); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index c723ea7f7ff6..9e7aa25572ed 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -6957,7 +6957,7 @@ public void visitGroupedPredicate(GroupedPredicate groupedPredicate) { public void visitInListPredicate(InListPredicate inListPredicate) { final List listExpressions = inListPredicate.getListExpressions(); if ( listExpressions.isEmpty() ) { - appendSql( "1=0" ); + appendSql( "1=" + ( inListPredicate.isNegated() ? "1" : "0" ) ); return; } Function itemAccessor = Function.identity(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/QueryTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/QueryTest.java index f43c72e7f20f..44e3b0e0699d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/QueryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/QueryTest.java @@ -22,6 +22,7 @@ import jakarta.persistence.Query; import jakarta.persistence.TemporalType; import jakarta.persistence.Tuple; +import jakarta.persistence.TypedQuery; import org.hibernate.Hibernate; import org.hibernate.QueryException; @@ -38,6 +39,7 @@ import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.junit.Jira; import org.junit.Test; import junit.framework.Assert; @@ -894,6 +896,54 @@ public void testParameterListInExistingParens() throws Exception { } } + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-17490" ) + public void testEmptyParameterList() throws Exception { + final Item item = new Item( "Mouse", "Micro$oft mouse" ); + final Item item2 = new Item( "Computer", "Dell computer" ); + + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + try { + em.persist( item ); + em.persist( item2 ); + assertTrue( em.contains( item ) ); + em.getTransaction().commit(); + + em.getTransaction().begin(); + TypedQuery q = em.createQuery( + "select item from Item item where item.name in :names", + Item.class + ); + q.setParameter( "names", List.of() ); + List result = q.getResultList(); + assertNotNull( result ); + assertEquals( 0, result.size() ); + + q = em.createQuery( + "select item from Item item where item.name not in :names", + Item.class + ); + q.setParameter( "names", List.of() ); + result = q.getResultList(); + assertNotNull( result ); + assertEquals( 2, result.size() ); + + em.remove( result.get( 0 ) ); + em.remove( result.get( 1 ) ); + em.getTransaction().commit(); + } + catch (Exception e){ + if ( em.getTransaction() != null && em.getTransaction().isActive() ) { + em.getTransaction().rollback(); + } + throw e; + } + finally { + em.close(); + } + } + @Test public void testEscapeCharacter() throws Exception { final Item item = new Item( "Mouse", "Micro_oft mouse" ); From c9f79733bcc6751953c2b0951b1596f2640bac36 Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Fri, 5 Jan 2024 22:02:55 +0100 Subject: [PATCH 014/321] HHH-17616 - Move resources that were still in src/java to src/resources (hibernate-core) Signed-off-by: Jan Schatteman --- hibernate-core/hibernate-core.gradle | 2 - .../cid/Mappings.hbm.xml | 0 .../propertyref/Mappings.hbm.xml | 0 .../e1/b/specjmapid/lazy/hbm_order.xml | 0 .../e1/b/specjmapid/lazy/order_orm.xml | 0 .../any/hbm/AnyTestEagerPropertySet.hbm.xml | 0 .../any/hbm/AnyTestLazyPropertySet.hbm.xml | 0 .../hibernate/orm/test/any/hbm/Person.hbm.xml | 0 .../orm/test/any/hbm/Properties.hbm.xml | 0 .../org/hibernate/orm/test/array/A.hbm.xml | 0 .../orm/test/batch/DataPoint.hbm.xml | 0 .../orm/test/batchfetch/ProductLine.hbm.xml | 0 .../hibernate/orm/test/bidi/Auction.hbm.xml | 0 .../hibernate/orm/test/bidi/Auction2.hbm.xml | 0 .../hibernate/orm/test/bidi/Auction3.hbm.xml | 0 .../orm/test/boot/cfgXml/badnamespace.cfg.xml | 0 .../orm/test/boot/cfgXml/hibernate.cfg.xml | 0 .../dynamic/SimpleDynamicEntity.hbm.xml | 0 .../hbm/simple/pojo/SimpleEntity.hbm.xml | 0 .../hibernate/orm/test/bytecode/Bean.hbm.xml | 0 .../hibernate/orm/test/cascade/Child.hbm.xml | 0 .../ChildForParentWithAssignedId.hbm.xml | 0 .../test/cascade/DeleteOrphanChild.hbm.xml | 0 .../hibernate/orm/test/cascade/Job.hbm.xml | 0 .../orm/test/cascade/JobBatch.hbm.xml | 0 .../orm/test/cascade/MultiPathCascade.hbm.xml | 0 .../hibernate/orm/test/cascade/Parent.hbm.xml | 0 .../test/cascade/ParentWithAssignedId.hbm.xml | 0 .../circle/CascadeManagedAndTransient.hbm.xml | 0 .../CascadeMergeToChildBeforeParent.hbm.xml | 0 .../circle/MultiPathCircleCascade.hbm.xml | 0 ...ultiPathCircleCascadeDelayedInsert.hbm.xml | 0 .../hibernate/orm/test/cfg/Cacheable.hbm.xml | 0 .../hibernate/orm/test/cfg/FooEntity.hbm.xml | 0 .../orm/test/cfg/cache/BaseClass.hbm.xml | 0 .../hibernate/orm/test/cfg/cache/Item.hbm.xml | 0 .../orm/test/cfg/cache/SubClass.hbm.xml | 0 .../orm/test/cfg/cache/hibernate.cfg.xml | 0 .../hibernate/orm/test/cid/Customer.hbm.xml | 0 .../hibernate/orm/test/cid/LineItem.hbm.xml | 0 .../org/hibernate/orm/test/cid/Order.hbm.xml | 0 .../hibernate/orm/test/cid/Product.hbm.xml | 0 .../orm/test/cid/PurchaseRecord.hbm.xml | 0 .../backref/map/compkey/Mappings.hbm.xml | 0 .../orm/test/collection/bag/Mappings.hbm.xml | 0 .../test/collection/idbag/Mappings.hbm.xml | 0 .../collection/lazynocascade/Parent.hbm.xml | 0 .../orm/test/collection/list/Mappings.hbm.xml | 0 .../list/ParentChildMapping.hbm.xml | 0 .../orm/test/collection/map/Mappings.hbm.xml | 0 .../original/UserPermissions.hbm.xml | 0 .../orm/test/collection/original/Zoo.hbm.xml | 0 .../test/collection/propertyref/Mail.hbm.xml | 0 .../test/collection/propertyref/User.hbm.xml | 0 .../collection/propertyref/lazy/Mail.hbm.xml | 0 .../collection/propertyref/lazy/User.hbm.xml | 0 .../orm/test/collection/set/Mappings.hbm.xml | 0 .../collection/set/MappingsNonLazy.hbm.xml | 0 .../orm/test/component/basic/User.hbm.xml | 0 .../cascading/collection/Mappings.hbm.xml | 0 .../cascading/toone/Mappings.hbm.xml | 0 .../orm/test/compositeelement/Parent.hbm.xml | 0 .../orm/test/connections/Silly.hbm.xml | 0 .../org/hibernate/orm/test/cuk/Person.hbm.xml | 0 .../orm/test/cut/Transaction.hbm.xml | 0 .../orm/test/deletetransient/Person.hbm.xml | 0 .../orm/test/dialect/function/Product.hbm.xml | 0 .../orm/test/dynamicmap/Test.hbm.xml | 116 +++++++++--------- .../hibernate/orm/test/ecid/Course.hbm.xml | 0 .../orm/test/entitygraph/named/basic/orm.xml | 0 .../test/entitygraph/named/subgraph/orm.xml | 0 .../orm/test/entitygraph/xml/orm.xml | 0 .../entitymode/map/basic/ProductLine.hbm.xml | 0 .../entitymode/map/compositeId/CompId.hbm.xml | 0 .../entitymode/map/subclass/Mappings.hbm.xml | 0 .../orm/test/entityname/MyEntity.hbm.xml | 0 .../orm/test/entityname/Vehicle.hbm.xml | 0 ...rectionalManyToManyBagToSetMapping.hbm.xml | 0 ...rectionalManyToManySetToSetMapping.hbm.xml | 0 .../BidirectionalOneToManyBagMapping.hbm.xml | 0 ...ctionalOneToManyBagSubclassMapping.hbm.xml | 0 .../BidirectionalOneToManySetMapping.hbm.xml | 0 ...UnidirectionalManyToManyBagMapping.hbm.xml | 0 .../UnidirectionalOneToManyBagMapping.hbm.xml | 0 .../UnidirectionalOneToManySetMapping.hbm.xml | 0 .../MultipleCollectionBagMapping.hbm.xml | 0 .../values/ValuesBagMapping.hbm.xml | 0 .../test/events/DefaultEntityListener-orm.xml | 0 .../orm/test/exception/Group.hbm.xml | 0 .../hibernate/orm/test/exception/User.hbm.xml | 0 .../orm/test/extendshbm/Customer.hbm.xml | 0 .../orm/test/extendshbm/Employee.hbm.xml | 0 .../orm/test/extendshbm/Person.hbm.xml | 0 .../orm/test/extendshbm/allinone.hbm.xml | 0 .../test/extendshbm/allseparateinone.hbm.xml | 0 .../orm/test/extendshbm/entitynames.hbm.xml | 0 .../extendshbm/packageentitynames.hbm.xml | 0 .../orm/test/extendshbm/unionsubclass.hbm.xml | 0 .../orm/test/extralazy/Child.hbm.xml | 0 .../orm/test/extralazy/Parent.hbm.xml | 0 .../orm/test/extralazy/UserGroup.hbm.xml | 0 .../test/fetchprofiles/join/Mappings.hbm.xml | 0 .../orm/test/filter/Category.hbm.xml | 0 .../orm/test/filter/Department.hbm.xml | 0 .../orm/test/filter/LineItem.hbm.xml | 0 .../hibernate/orm/test/filter/Order.hbm.xml | 0 .../hibernate/orm/test/filter/Product.hbm.xml | 0 .../orm/test/filter/Salesperson.hbm.xml | 0 .../hibernate/orm/test/filter/defs.hbm.xml | 0 .../orm/test/formulajoin/Root.hbm.xml | 0 .../generatedkeys/generated/MyEntity.hbm.xml | 0 .../generatedkeys/identity/MyEntity.hbm.xml | 0 .../generatedkeys/select/MyEntity.hbm.xml | 0 .../selectannotated/MyEntity.hbm.xml | 0 .../test/hbm/inheritance/AnimalReport.hbm.xml | 0 .../unmapped_association.hbm.xml | 0 .../unmapped_collection.hbm.xml | 0 .../hbm/query/HbmOverridesAnnotation.hbm.xml | 0 .../hbm/query/HbmOverridesAnnotation.orm.xml | 0 .../org/hibernate/orm/test/hql/Animal.hbm.xml | 0 .../orm/test/hql/BooleanLiteralEntity.hbm.xml | 0 .../orm/test/hql/ComponentContainer.hbm.xml | 0 .../orm/test/hql/CompositeIdEntity.hbm.xml | 0 .../orm/test/hql/Constructor.hbm.xml | 0 .../orm/test/hql/CrazyIdFieldNames.hbm.xml | 0 .../hql/EntityWithCrazyCompositeKey.hbm.xml | 0 .../hibernate/orm/test/hql/FooBarCopy.hbm.xml | 0 .../test/hql/FunctionNamesAsColumns.hbm.xml | 0 .../org/hibernate/orm/test/hql/Image.hbm.xml | 0 .../orm/test/hql/KeyManyToOneEntity.hbm.xml | 0 .../hibernate/orm/test/hql/Properties.hbm.xml | 0 .../hql/SimpleEntityWithAssociation.hbm.xml | 0 .../hql/VariousKeywordPropertyEntity.hbm.xml | 0 .../hibernate/orm/test/hql/Vehicle.hbm.xml | 0 .../hibernate/orm/test/hql/Versions.hbm.xml | 0 .../test/hqlfetchscroll/ParentChild.hbm.xml | 82 ++++++------- .../org/hibernate/orm/test/id/Person.hbm.xml | 0 .../org/hibernate/orm/test/id/Product.hbm.xml | 0 .../orm/test/id/SQLServer2012Person.hbm.xml | 0 .../orm/test/idbag/UserGroup.hbm.xml | 0 .../orm/test/idclass/Customer.hbm.xml | 0 .../biginteger/increment/Mapping.hbm.xml | 0 .../idgen/biginteger/sequence/Mapping.hbm.xml | 0 .../sequence/ZeroScaleMapping.hbm.xml | 0 .../idgen/enhanced/forcedtable/Basic.hbm.xml | 0 .../idgen/enhanced/forcedtable/HiLo.hbm.xml | 0 .../idgen/enhanced/forcedtable/Pooled.hbm.xml | 0 .../idgen/enhanced/sequence/Basic.hbm.xml | 0 .../idgen/enhanced/sequence/Dedicated.hbm.xml | 0 .../test/idgen/enhanced/sequence/HiLo.hbm.xml | 0 .../idgen/enhanced/sequence/Pooled.hbm.xml | 0 .../test/idgen/enhanced/table/Basic.hbm.xml | 0 .../test/idgen/enhanced/table/HiLo.hbm.xml | 0 .../test/idgen/enhanced/table/Pooled.hbm.xml | 0 .../orm/test/idprops/Mapping.hbm.xml | 0 .../test/immutable/ContractVariation.hbm.xml | 0 .../inverse/ContractVariation.hbm.xml | 0 .../ContractVariationOneToManyJoin.hbm.xml | 0 .../ContractVariationVersioned.hbm.xml | 0 ...actVariationVersionedOneToManyJoin.hbm.xml | 0 .../noninverse/ContractVariation.hbm.xml | 0 .../ContractVariationOneToManyJoin.hbm.xml | 0 .../ContractVariationUnidir.hbm.xml | 0 .../ContractVariationVersioned.hbm.xml | 0 ...actVariationVersionedOneToManyJoin.hbm.xml | 0 .../joinedsubclass/TestEntity.hbm.xml | 0 .../orm/test/insertordering/Mapping.hbm.xml | 0 .../orm/test/interceptor/Image.hbm.xml | 0 .../orm/test/interceptor/User.hbm.xml | 0 .../orm/test/interfaceproxy/Item.hbm.xml | 0 .../hibernate/orm/test/iterate/Item.hbm.xml | 0 .../hibernate/orm/test/jdbc/Mappings.hbm.xml | 0 .../hibernate/orm/test/join/Person.hbm.xml | 0 .../orm/test/join/Reportable.hbm.xml | 0 .../org/hibernate/orm/test/join/Thing.hbm.xml | 0 .../orm/test/joinedsubclass/Person.hbm.xml | 0 .../orm/test/jpa/cascade2/ParentChild.hbm.xml | 0 .../jpa/compliance/callback/listener/orm.xml | 0 .../orm/test/jpa/fetch/Person.hbm.xml | 0 .../hibernate/orm/test/jpa/model/Item.hbm.xml | 0 .../orm/test/jpa/model/MyEntity.hbm.xml | 0 .../hibernate/orm/test/jpa/model/Part.hbm.xml | 0 .../orm/test/jpa/naturalid/User.hbm.xml | 0 ...rCollectionLazyKeyManyToOneMapping.hbm.xml | 0 .../bidir/component/EagerMapping.hbm.xml | 0 .../bidir/component/LazyMapping.hbm.xml | 0 .../bidir/embedded/Mapping.hbm.xml | 0 .../bidir/ondelete/Mapping.hbm.xml | 0 .../unidir/ondelete/Mapping.hbm.xml | 0 .../orm/test/lazyonetoone/Person.hbm.xml | 0 .../org/hibernate/orm/test/legacy/ABC.hbm.xml | 0 .../orm/test/legacy/ABCExtends.hbm.xml | 0 .../orm/test/legacy/ABCProxy.hbm.xml | 0 .../orm/test/legacy/AltSimple.hbm.xml | 0 .../org/hibernate/orm/test/legacy/Baz.hbm.xml | 0 .../hibernate/orm/test/legacy/Blobber.hbm.xml | 0 .../hibernate/orm/test/legacy/Broken.hbm.xml | 0 .../orm/test/legacy/Category.hbm.xml | 0 .../orm/test/legacy/Circular.hbm.xml | 0 .../orm/test/legacy/Commento.hbm.xml | 0 .../test/legacy/ComponentNotNullRoot.hbm.xml | 0 .../orm/test/legacy/Componentizable.hbm.xml | 0 .../orm/test/legacy/CompositeIdId.hbm.xml | 0 .../orm/test/legacy/Container.hbm.xml | 0 .../hibernate/orm/test/legacy/Custom.hbm.xml | 0 .../orm/test/legacy/CustomSQL.hbm.xml | 0 .../org/hibernate/orm/test/legacy/Eye.hbm.xml | 0 .../org/hibernate/orm/test/legacy/Fee.hbm.xml | 0 .../org/hibernate/orm/test/legacy/Fo.hbm.xml | 0 .../hibernate/orm/test/legacy/FooBar.hbm.xml | 0 .../org/hibernate/orm/test/legacy/Fum.hbm.xml | 0 .../hibernate/orm/test/legacy/Fumm.hbm.xml | 0 .../hibernate/orm/test/legacy/Glarch.hbm.xml | 0 .../hibernate/orm/test/legacy/Holder.hbm.xml | 0 .../org/hibernate/orm/test/legacy/IJ.hbm.xml | 0 .../org/hibernate/orm/test/legacy/IJ2.hbm.xml | 0 .../orm/test/legacy/Immutable.hbm.xml | 0 .../orm/test/legacy/Location.hbm.xml | 0 .../orm/test/legacy/MainObject.hbm.xml | 0 .../hibernate/orm/test/legacy/Many.hbm.xml | 0 .../org/hibernate/orm/test/legacy/Map.hbm.xml | 0 .../hibernate/orm/test/legacy/Marelo.hbm.xml | 0 .../hibernate/orm/test/legacy/Middle.hbm.xml | 0 .../hibernate/orm/test/legacy/Multi.hbm.xml | 0 .../orm/test/legacy/MultiExtends.hbm.xml | 0 .../orm/test/legacy/Nameable.hbm.xml | 0 .../hibernate/orm/test/legacy/Object2.hbm.xml | 0 .../org/hibernate/orm/test/legacy/One.hbm.xml | 0 .../orm/test/legacy/ParentChild.hbm.xml | 0 .../org/hibernate/orm/test/legacy/Qux.hbm.xml | 0 .../orm/test/legacy/RootDetail.hbm.xml | 0 .../hibernate/orm/test/legacy/Simple.hbm.xml | 0 .../orm/test/legacy/SingleSeveral.hbm.xml | 0 .../hibernate/orm/test/legacy/Stuff.hbm.xml | 0 .../hibernate/orm/test/legacy/UpDown.hbm.xml | 0 .../hibernate/orm/test/legacy/Vetoer.hbm.xml | 0 .../org/hibernate/orm/test/legacy/WZ.hbm.xml | 0 .../hibernate/orm/test/legacy/Wicked.hbm.xml | 0 .../org/hibernate/orm/test/legacy/XY.hbm.xml | 0 .../orm/test/lob/ImageMappings.hbm.xml | 0 .../orm/test/lob/LobAsLastValue.hbm.xml | 0 .../orm/test/lob/LobMappings.hbm.xml | 0 .../test/lob/MaterializedBlobMappings.hbm.xml | 0 .../test/lob/MaterializedClobMappings.hbm.xml | 0 .../orm/test/lob/SerializableMappings.hbm.xml | 0 .../orm/test/lob/TextMappings.hbm.xml | 0 .../orm/test/manytomany/UserGroup.hbm.xml | 0 .../batchload/UserGroupBatchLoad.hbm.xml | 0 .../test/manytomany/ordered/UserGroup.hbm.xml | 0 .../compositeid/Mappings.hbm.xml | 0 .../nestedreference/Item.hbm.xml | 0 .../surrogateid/assigned/Mappings.hbm.xml | 0 .../surrogateid/generated/Mappings.hbm.xml | 0 .../hibernate/orm/test/map/UserGroup.hbm.xml | 0 .../orm/test/mapelemformula/UserGroup.hbm.xml | 0 .../test/mapping/attrorder/mappings.hbm.xml | 0 .../custom/basic/UserPermissions.hbm.xml | 0 .../declaredtype/UserPermissions.hbm.xml | 0 .../custom/parameterized/Mapping.hbm.xml | 0 .../mapcompelem/ProductPart.hbm.xml | 0 .../contributed/BasicContributorTests.hbm.xml | 0 .../mapping/formula/EntityOfFormulas.hbm.xml | 0 .../mapping/generated/ComponentOwner.hbm.xml | 0 .../generated/GeneratedPropertyEntity.hbm.xml | 0 .../MSSQLGeneratedPropertyEntity.hbm.xml | 0 .../discriminator/AccountOwner.hbm.xml | 0 .../inheritance/discriminator/Person.hbm.xml | 0 .../discriminator/SimpleInheritance.hbm.xml | 0 .../joined/JoinedSubclassInheritance.hbm.xml | 0 .../dynamic/JoinedMappings.hbm.xml | 0 .../mapping/inheritance/joined/Person.hbm.xml | 0 .../mapping/naturalid/cid/Account.hbm.xml | 0 .../ParentChildWithManyToOne.hbm.xml | 0 .../mapping/naturalid/immutable/User.hbm.xml | 0 .../mapping/naturalid/mutable/User.hbm.xml | 0 .../mapping/naturalid/nullable/User.hbm.xml | 0 .../mapping/readwrite/ReadWriteEntity.hbm.xml | 0 .../mapping/typedmanytoone/Customer.hbm.xml | 0 .../test/mapping/usertypes/TestEntity.hbm.xml | 0 .../mapping/usertypes/TestEnumType.hbm.xml | 0 .../mapping/usertypes/xmlmapping/entities.xml | 0 .../mappingexception/InvalidMapping.hbm.xml | 0 .../orm/test/mappingexception/User.hbm.xml | 0 .../org/hibernate/orm/test/math/Math.hbm.xml | 0 .../org/hibernate/orm/test/mixed/Item.hbm.xml | 0 .../orm/test/namingstrategy/Customers.hbm.xml | 0 .../namingstrategy/complete/Mappings.hbm.xml | 0 .../NonPkManyToOneAssociationHbmTest.hbm.xml | 0 .../orm/test/ondelete/Person.hbm.xml | 0 .../ondelete/toone/hbm/ToOneOnDelete.hbm.xml | 0 .../hibernate/orm/test/onetomany/Node.hbm.xml | 0 .../orm/test/onetomany/Parent.hbm.xml | 0 .../orm/test/onetomany/VersionedNode.hbm.xml | 0 .../orm/test/onetoone/cache/Details.hbm.xml | 0 .../test/onetoone/cache/MainObject.hbm.xml | 0 .../orm/test/onetoone/cache/Object2.hbm.xml | 0 .../orm/test/onetoone/cache/Person.hbm.xml | 0 .../orm/test/onetoone/formula/Person.hbm.xml | 0 .../orm/test/onetoone/joined/Person.hbm.xml | 0 .../orm/test/onetoone/link/Person.hbm.xml | 0 .../orm/test/onetoone/nopojo/Person.hbm.xml | 0 .../orm/test/onetoone/optional/Person.hbm.xml | 0 .../test/onetoone/singletable/Person.hbm.xml | 0 .../orm/test/ops/Competition.hbm.xml | 0 .../hibernate/orm/test/ops/Employer.hbm.xml | 0 .../hibernate/orm/test/ops/Hoarder.hbm.xml | 0 .../orm/test/ops/HoarderOrphanDelete.hbm.xml | 0 .../org/hibernate/orm/test/ops/Node.hbm.xml | 0 .../hibernate/orm/test/ops/OneToOne.hbm.xml | 0 .../orm/test/ops/OptLockEntity.hbm.xml | 0 .../orm/test/ops/SimpleEntity.hbm.xml | 0 .../orm/test/optlock/Document.hbm.xml | 0 .../hibernate/orm/test/ordered/Search.hbm.xml | 0 .../hibernate/orm/test/orphan/Mail.hbm.xml | 0 .../hibernate/orm/test/orphan/Product.hbm.xml | 0 .../hibernate/orm/test/orphan/User.hbm.xml | 0 .../orphan/elementcollection/student.hbm.xml | 0 .../test/orphan/manytomany/UserGroup.hbm.xml | 62 +++++----- .../one2one/fk/bidirectional/Mapping.hbm.xml | 0 .../one2one/fk/composite/Mapping.hbm.xml | 0 .../fk/reversed/bidirectional/Mapping.hbm.xml | 0 .../reversed/unidirectional/Mapping.hbm.xml | 0 .../one2one/pk/bidirectional/Mapping.hbm.xml | 0 .../one2one/pk/unidirectional/Mapping.hbm.xml | 0 .../orm/test/propertyref/Mapping.hbm.xml | 0 .../propertyref/basic/EntityClass.hbm.xml | 54 ++++---- .../orm/test/propertyref/basic/Person.hbm.xml | 0 .../cachedcollections/Mappings.hbm.xml | 0 .../component/complete/Mapping.hbm.xml | 0 .../inheritence/discrim/Person.hbm.xml | 0 .../inheritence/joined/Person.hbm.xml | 0 .../inheritence/union/Person.hbm.xml | 0 .../test/propertyref/partial/Mapping.hbm.xml | 0 .../orm/test/proxy/DataPoint.hbm.xml | 0 .../orm/test/query/joinfetch/ItemBid.hbm.xml | 0 .../test/query/joinfetch/UserGroup.hbm.xml | 0 .../resultmapping/result-set-mapping.hbm.xml | 0 .../orm/test/querycache/Enrolment.hbm.xml | 0 .../orm/test/querycache/Item.hbm.xml | 0 .../orm/test/quote/DataPoint.hbm.xml | 0 .../orm/test/readonly/DataPoint.hbm.xml | 0 .../orm/test/readonly/Enrolment.hbm.xml | 0 .../orm/test/readonly/TextHolder.hbm.xml | 0 .../orm/test/readonly/VersionedNode.hbm.xml | 0 .../orm/test/reattachment/Mappings.hbm.xml | 0 .../orm/test/refresh/Customer.hbm.xml | 0 .../test/resulttransformer/Contract.hbm.xml | 0 .../hibernate/orm/test/rowid/Point.hbm.xml | 0 .../test/schema/SchemaGenerationTest.hbm.xml | 0 .../orm/test/schemaupdate/1_Version.hbm.xml | 0 .../orm/test/schemaupdate/2_Version.hbm.xml | 0 .../orm/test/schemaupdate/3_Version.hbm.xml | 0 .../orm/test/schemaupdate/4_Version.hbm.xml | 0 .../schemaupdate/CommentGeneration.hbm.xml | 0 .../orm/test/schemaupdate/UserGroup.hbm.xml | 0 .../test/schemaupdate/idbag/Mappings.hbm.xml | 0 .../schemaupdate/idgenerator/sequence.hbm.xml | 0 .../schemaupdate/inheritance/Employee.hbm.xml | 0 .../schemaupdate/inheritance/Manager.hbm.xml | 0 .../schemaupdate/inheritance/Payment.hbm.xml | 0 .../schemaupdate/inheritance/Person.hbm.xml | 0 .../schemaupdate/manytomany/UserGroup.hbm.xml | 0 .../orm/test/schemaupdate/mapping.hbm.xml | 0 .../orm/test/schemaupdate/mapping2.hbm.xml | 0 .../uniqueconstraint/TestEntity.hbm.xml | 0 .../test/sql/check/oracle-mappings.hbm.xml | 0 .../oracle/StoredProcedures.hbm.xml | 0 .../test/sql/hand/custom/db2/Mappings.hbm.xml | 0 .../sql/hand/custom/derby/Mappings.hbm.xml | 0 .../sql/hand/custom/mysql/Mappings.hbm.xml | 0 .../sql/hand/custom/oracle/Mappings.hbm.xml | 0 .../custom/oracle/StoredProcedures.hbm.xml | 0 .../hand/custom/sqlserver/Mappings.hbm.xml | 0 .../sql/hand/custom/sybase/Mappings.hbm.xml | 0 .../test/sql/hand/identity/Mappings.hbm.xml | 0 .../sql/hand/query/NativeSQLQueries.hbm.xml | 0 .../hand/quotedidentifiers/Mappings.hbm.xml | 0 .../orm/test/stateless/Contact.hbm.xml | 0 .../orm/test/stateless/Document.hbm.xml | 0 .../test/stateless/fetching/Mappings.hbm.xml | 0 .../test/stateless/insert/Mappings.hbm.xml | 0 .../orm/test/stats/Continent.hbm.xml | 0 .../orm/test/stats/Continent2.hbm.xml | 0 .../subclassProxyInterface/Person.hbm.xml | 0 .../subclassfilter/discrim-subclass.hbm.xml | 0 .../subclassfilter/joined-subclass.hbm.xml | 0 .../subclassfilter/union-subclass.hbm.xml | 0 .../orm/test/subselect/Beings.hbm.xml | 0 .../hibernate/orm/test/subselect/Book.hbm.xml | 0 .../orm/test/subselect/join/Order.hbm.xml | 0 .../orm/test/ternary/Ternary.hbm.xml | 0 .../hibernate/orm/test/timestamp/User.hbm.xml | 0 .../org/hibernate/orm/test/tm/Item.hbm.xml | 0 .../orm/test/typedonetoone/Customer.hbm.xml | 0 .../orm/test/typeoverride/Entity.hbm.xml | 0 .../orm/test/typeparameters/Typedef.hbm.xml | 0 .../orm/test/typeparameters/Widget.hbm.xml | 0 .../orm/test/unconstrained/Person.hbm.xml | 0 .../orm/test/unidir/ParentChild.hbm.xml | 0 .../unidir/ParentChildPropertyRef.hbm.xml | 0 .../orm/test/unionsubclass/Beings.hbm.xml | 0 .../test/unionsubclass/alias/mapping.hbm.xml | 0 .../orm/test/unionsubclass2/Person.hbm.xml | 0 .../orm/test/util/dtd/Parent.hbm.xml | 0 .../org/hibernate/orm/test/util/dtd/child.xml | 0 .../orm/test/version/PersonThing.hbm.xml | 0 .../orm/test/version/db/User.hbm.xml | 0 .../mappedsuperclass/TestEntity.hbm.xml | 0 .../orm/test/version/sybase/User.hbm.xml | 0 ...gerManyToOneFetchModeJoinWhereTest.hbm.xml | 0 ...rManyToOneFetchModeSelectWhereTest.hbm.xml | 0 .../test/where/hbm/EagerToManyWhere.hbm.xml | 0 .../hibernate/orm/test/where/hbm/File.hbm.xml | 0 ...ollectionBasicNonUniqueIdWhereTest.hbm.xml | 0 ...hLazyManyToOneNonUniqueIdWhereTest.hbm.xml | 0 ...ToManyNonUniqueIdNotFoundWhereTest.hbm.xml | 0 ...LazyManyToManyNonUniqueIdWhereTest.hbm.xml | 0 .../LazyOneToManyNonUniqueIdWhereTest.hbm.xml | 0 .../test/where/hbm/LazyToManyWhere.hbm.xml | 0 419 files changed, 157 insertions(+), 159 deletions(-) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/abstractembeddedcomponents/cid/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/abstractembeddedcomponents/propertyref/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/annotations/derivedidentities/e1/b/specjmapid/lazy/hbm_order.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/annotations/derivedidentities/e1/b/specjmapid/lazy/order_orm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/any/hbm/AnyTestEagerPropertySet.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/any/hbm/AnyTestLazyPropertySet.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/any/hbm/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/any/hbm/Properties.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/array/A.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/batch/DataPoint.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/batchfetch/ProductLine.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/bidi/Auction.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/bidi/Auction2.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/bidi/Auction3.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/boot/cfgXml/badnamespace.cfg.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/boot/cfgXml/hibernate.cfg.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/bootstrap/binding/hbm/simple/dynamic/SimpleDynamicEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/bootstrap/binding/hbm/simple/pojo/SimpleEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/bytecode/Bean.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cascade/Child.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cascade/ChildForParentWithAssignedId.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cascade/DeleteOrphanChild.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cascade/Job.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cascade/JobBatch.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cascade/MultiPathCascade.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cascade/Parent.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cascade/ParentWithAssignedId.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cascade/circle/CascadeManagedAndTransient.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cascade/circle/CascadeMergeToChildBeforeParent.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cascade/circle/MultiPathCircleCascade.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cascade/circle/MultiPathCircleCascadeDelayedInsert.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cfg/Cacheable.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cfg/FooEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cfg/cache/BaseClass.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cfg/cache/Item.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cfg/cache/SubClass.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cfg/cache/hibernate.cfg.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cid/Customer.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cid/LineItem.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cid/Order.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cid/Product.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cid/PurchaseRecord.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/collection/backref/map/compkey/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/collection/bag/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/collection/idbag/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/collection/lazynocascade/Parent.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/collection/list/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/collection/list/ParentChildMapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/collection/map/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/collection/original/UserPermissions.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/collection/original/Zoo.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/collection/propertyref/Mail.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/collection/propertyref/User.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/collection/propertyref/lazy/Mail.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/collection/propertyref/lazy/User.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/collection/set/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/collection/set/MappingsNonLazy.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/component/basic/User.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/component/cascading/collection/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/component/cascading/toone/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/compositeelement/Parent.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/connections/Silly.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cuk/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/cut/Transaction.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/deletetransient/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/dialect/function/Product.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/dynamicmap/Test.hbm.xml (97%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/ecid/Course.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/entitygraph/named/basic/orm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/entitygraph/named/subgraph/orm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/entitygraph/xml/orm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/entitymode/map/basic/ProductLine.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/entitymode/map/compositeId/CompId.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/entitymode/map/subclass/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/entityname/MyEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/entityname/Vehicle.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/event/collection/association/bidirectional/manytomany/BidirectionalManyToManyBagToSetMapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/event/collection/association/bidirectional/manytomany/BidirectionalManyToManySetToSetMapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/event/collection/association/bidirectional/onetomany/BidirectionalOneToManyBagMapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/event/collection/association/bidirectional/onetomany/BidirectionalOneToManyBagSubclassMapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/event/collection/association/bidirectional/onetomany/BidirectionalOneToManySetMapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/event/collection/association/unidirectional/manytomany/UnidirectionalManyToManyBagMapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/event/collection/association/unidirectional/onetomany/UnidirectionalOneToManyBagMapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/event/collection/association/unidirectional/onetomany/UnidirectionalOneToManySetMapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/event/collection/detached/MultipleCollectionBagMapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/event/collection/values/ValuesBagMapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/events/DefaultEntityListener-orm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/exception/Group.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/exception/User.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/extendshbm/Customer.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/extendshbm/Employee.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/extendshbm/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/extendshbm/allinone.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/extendshbm/allseparateinone.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/extendshbm/entitynames.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/extendshbm/packageentitynames.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/extendshbm/unionsubclass.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/extralazy/Child.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/extralazy/Parent.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/extralazy/UserGroup.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/fetchprofiles/join/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/filter/Category.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/filter/Department.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/filter/LineItem.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/filter/Order.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/filter/Product.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/filter/Salesperson.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/filter/defs.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/formulajoin/Root.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/generatedkeys/generated/MyEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/generatedkeys/identity/MyEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/generatedkeys/select/MyEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/generatedkeys/selectannotated/MyEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hbm/inheritance/AnimalReport.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hbm/mappingexception/unmapped_association.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hbm/mappingexception/unmapped_collection.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.orm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hql/Animal.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hql/BooleanLiteralEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hql/ComponentContainer.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hql/CompositeIdEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hql/Constructor.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hql/CrazyIdFieldNames.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hql/EntityWithCrazyCompositeKey.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hql/FooBarCopy.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hql/FunctionNamesAsColumns.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hql/Image.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hql/KeyManyToOneEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hql/Properties.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hql/SimpleEntityWithAssociation.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hql/VariousKeywordPropertyEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hql/Vehicle.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hql/Versions.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/hqlfetchscroll/ParentChild.hbm.xml (96%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/id/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/id/Product.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/id/SQLServer2012Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/idbag/UserGroup.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/idclass/Customer.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/idgen/biginteger/increment/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/idgen/biginteger/sequence/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/idgen/biginteger/sequence/ZeroScaleMapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/idgen/enhanced/forcedtable/Basic.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/idgen/enhanced/forcedtable/HiLo.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/idgen/enhanced/forcedtable/Pooled.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/idgen/enhanced/sequence/Basic.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/idgen/enhanced/sequence/Dedicated.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/idgen/enhanced/sequence/HiLo.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/idgen/enhanced/sequence/Pooled.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/idgen/enhanced/table/Basic.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/idgen/enhanced/table/HiLo.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/idgen/enhanced/table/Pooled.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/idprops/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/immutable/ContractVariation.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariation.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariationOneToManyJoin.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariationVersioned.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariationVersionedOneToManyJoin.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariation.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationOneToManyJoin.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationUnidir.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationVersioned.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationVersionedOneToManyJoin.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/inheritance/discriminator/joinedsubclass/TestEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/insertordering/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/interceptor/Image.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/interceptor/User.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/interfaceproxy/Item.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/iterate/Item.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/jdbc/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/join/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/join/Reportable.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/join/Thing.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/joinedsubclass/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/jpa/cascade2/ParentChild.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/jpa/compliance/callback/listener/orm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/jpa/fetch/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/jpa/model/Item.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/jpa/model/MyEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/jpa/model/Part.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/jpa/naturalid/User.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/keymanytoone/bidir/component/EagerCollectionLazyKeyManyToOneMapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/keymanytoone/bidir/component/EagerMapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/keymanytoone/bidir/component/LazyMapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/keymanytoone/bidir/embedded/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/keymanytoone/bidir/ondelete/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/keymanytoone/unidir/ondelete/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/lazyonetoone/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/ABC.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/ABCExtends.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/ABCProxy.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/AltSimple.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Baz.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Blobber.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Broken.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Category.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Circular.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Commento.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/ComponentNotNullRoot.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Componentizable.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/CompositeIdId.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Container.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Custom.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/CustomSQL.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Eye.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Fee.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Fo.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/FooBar.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Fum.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Fumm.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Glarch.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Holder.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/IJ.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/IJ2.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Immutable.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Location.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/MainObject.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Many.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Map.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Marelo.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Middle.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Multi.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/MultiExtends.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Nameable.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Object2.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/One.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/ParentChild.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Qux.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/RootDetail.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Simple.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/SingleSeveral.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Stuff.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/UpDown.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Vetoer.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/WZ.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/Wicked.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/legacy/XY.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/lob/ImageMappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/lob/LobAsLastValue.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/lob/LobMappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/lob/MaterializedBlobMappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/lob/MaterializedClobMappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/lob/SerializableMappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/lob/TextMappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/manytomany/UserGroup.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/manytomany/batchload/UserGroupBatchLoad.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/manytomany/ordered/UserGroup.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/manytomanyassociationclass/compositeid/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/manytomanyassociationclass/nestedreference/Item.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/manytomanyassociationclass/surrogateid/assigned/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/manytomanyassociationclass/surrogateid/generated/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/map/UserGroup.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapelemformula/UserGroup.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/attrorder/mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/collections/custom/basic/UserPermissions.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/collections/custom/declaredtype/UserPermissions.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/collections/custom/parameterized/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/collections/mapcompelem/ProductPart.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/contributed/BasicContributorTests.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/formula/EntityOfFormulas.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/generated/ComponentOwner.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/generated/GeneratedPropertyEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/generated/MSSQLGeneratedPropertyEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/inheritance/discriminator/AccountOwner.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/inheritance/discriminator/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/inheritance/discriminator/SimpleInheritance.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/inheritance/discriminator/joined/JoinedSubclassInheritance.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/inheritance/dynamic/JoinedMappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/inheritance/joined/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/naturalid/cid/Account.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/naturalid/immutable/ParentChildWithManyToOne.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/naturalid/immutable/User.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/naturalid/mutable/User.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/naturalid/nullable/User.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/readwrite/ReadWriteEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/typedmanytoone/Customer.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/usertypes/TestEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/usertypes/TestEnumType.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mapping/usertypes/xmlmapping/entities.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mappingexception/InvalidMapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mappingexception/User.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/math/Math.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/mixed/Item.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/namingstrategy/Customers.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/namingstrategy/complete/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/nonpkassociation/NonPkManyToOneAssociationHbmTest.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/ondelete/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/ondelete/toone/hbm/ToOneOnDelete.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/onetomany/Node.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/onetomany/Parent.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/onetomany/VersionedNode.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/onetoone/cache/Details.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/onetoone/cache/MainObject.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/onetoone/cache/Object2.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/onetoone/cache/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/onetoone/formula/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/onetoone/joined/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/onetoone/link/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/onetoone/nopojo/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/onetoone/optional/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/onetoone/singletable/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/ops/Competition.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/ops/Employer.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/ops/Hoarder.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/ops/HoarderOrphanDelete.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/ops/Node.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/ops/OneToOne.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/ops/OptLockEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/ops/SimpleEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/optlock/Document.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/ordered/Search.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/orphan/Mail.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/orphan/Product.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/orphan/User.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/orphan/elementcollection/student.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/orphan/manytomany/UserGroup.hbm.xml (96%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/orphan/one2one/fk/bidirectional/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/orphan/one2one/fk/composite/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/orphan/one2one/fk/reversed/bidirectional/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/orphan/one2one/fk/reversed/unidirectional/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/orphan/one2one/pk/bidirectional/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/orphan/one2one/pk/unidirectional/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/propertyref/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/propertyref/basic/EntityClass.hbm.xml (97%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/propertyref/basic/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/propertyref/cachedcollections/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/propertyref/component/complete/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/propertyref/inheritence/discrim/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/propertyref/inheritence/joined/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/propertyref/inheritence/union/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/propertyref/partial/Mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/proxy/DataPoint.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/query/joinfetch/ItemBid.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/query/joinfetch/UserGroup.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/query/resultmapping/result-set-mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/querycache/Enrolment.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/querycache/Item.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/quote/DataPoint.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/readonly/DataPoint.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/readonly/Enrolment.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/readonly/TextHolder.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/readonly/VersionedNode.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/reattachment/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/refresh/Customer.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/resulttransformer/Contract.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/rowid/Point.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schema/SchemaGenerationTest.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemaupdate/1_Version.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemaupdate/2_Version.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemaupdate/3_Version.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemaupdate/4_Version.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemaupdate/CommentGeneration.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemaupdate/UserGroup.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemaupdate/idbag/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemaupdate/idgenerator/sequence.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemaupdate/inheritance/Employee.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemaupdate/inheritance/Manager.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemaupdate/inheritance/Payment.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemaupdate/inheritance/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemaupdate/manytomany/UserGroup.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemaupdate/mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemaupdate/mapping2.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemaupdate/uniqueconstraint/TestEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/sql/check/oracle-mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/sql/hand/custom/datadirect/oracle/StoredProcedures.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/sql/hand/custom/db2/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/sql/hand/custom/derby/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/sql/hand/custom/mysql/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/sql/hand/custom/oracle/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/sql/hand/custom/oracle/StoredProcedures.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/sql/hand/custom/sqlserver/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/sql/hand/custom/sybase/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/sql/hand/identity/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/sql/hand/query/NativeSQLQueries.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/sql/hand/quotedidentifiers/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/stateless/Contact.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/stateless/Document.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/stateless/fetching/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/stateless/insert/Mappings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/stats/Continent.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/stats/Continent2.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/subclassProxyInterface/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/subclassfilter/discrim-subclass.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/subclassfilter/joined-subclass.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/subclassfilter/union-subclass.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/subselect/Beings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/subselect/Book.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/subselect/join/Order.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/ternary/Ternary.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/timestamp/User.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/tm/Item.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/typedonetoone/Customer.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/typeoverride/Entity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/typeparameters/Typedef.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/typeparameters/Widget.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/unconstrained/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/unidir/ParentChild.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/unidir/ParentChildPropertyRef.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/unionsubclass/Beings.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/unionsubclass/alias/mapping.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/unionsubclass2/Person.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/util/dtd/Parent.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/util/dtd/child.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/version/PersonThing.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/version/db/User.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/version/mappedsuperclass/TestEntity.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/version/sybase/User.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/where/hbm/EagerManyToOneFetchModeJoinWhereTest.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/where/hbm/EagerManyToOneFetchModeSelectWhereTest.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/where/hbm/EagerToManyWhere.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/where/hbm/File.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/where/hbm/LazyElementCollectionBasicNonUniqueIdWhereTest.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/where/hbm/LazyElementCollectionWithLazyManyToOneNonUniqueIdWhereTest.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/where/hbm/LazyManyToManyNonUniqueIdNotFoundWhereTest.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/where/hbm/LazyManyToManyNonUniqueIdWhereTest.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/where/hbm/LazyOneToManyNonUniqueIdWhereTest.hbm.xml (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/where/hbm/LazyToManyWhere.hbm.xml (100%) diff --git a/hibernate-core/hibernate-core.gradle b/hibernate-core/hibernate-core.gradle index 6af7b2bcb7d8..71865487d3e5 100644 --- a/hibernate-core/hibernate-core.gradle +++ b/hibernate-core/hibernate-core.gradle @@ -109,10 +109,8 @@ sourceSets { } } - // resources inherently exclude sources test { resources { - srcDir 'src/test/java' srcDir 'src/test/resources' srcDir 'src/test/bundles' } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/abstractembeddedcomponents/cid/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/abstractembeddedcomponents/cid/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/abstractembeddedcomponents/cid/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/abstractembeddedcomponents/cid/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/abstractembeddedcomponents/propertyref/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/abstractembeddedcomponents/propertyref/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/abstractembeddedcomponents/propertyref/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/abstractembeddedcomponents/propertyref/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/derivedidentities/e1/b/specjmapid/lazy/hbm_order.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/annotations/derivedidentities/e1/b/specjmapid/lazy/hbm_order.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/annotations/derivedidentities/e1/b/specjmapid/lazy/hbm_order.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/annotations/derivedidentities/e1/b/specjmapid/lazy/hbm_order.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/derivedidentities/e1/b/specjmapid/lazy/order_orm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/annotations/derivedidentities/e1/b/specjmapid/lazy/order_orm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/annotations/derivedidentities/e1/b/specjmapid/lazy/order_orm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/annotations/derivedidentities/e1/b/specjmapid/lazy/order_orm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/any/hbm/AnyTestEagerPropertySet.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/any/hbm/AnyTestEagerPropertySet.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/any/hbm/AnyTestEagerPropertySet.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/any/hbm/AnyTestEagerPropertySet.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/any/hbm/AnyTestLazyPropertySet.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/any/hbm/AnyTestLazyPropertySet.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/any/hbm/AnyTestLazyPropertySet.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/any/hbm/AnyTestLazyPropertySet.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/any/hbm/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/any/hbm/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/any/hbm/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/any/hbm/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/any/hbm/Properties.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/any/hbm/Properties.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/any/hbm/Properties.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/any/hbm/Properties.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/array/A.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/array/A.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/array/A.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/array/A.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/batch/DataPoint.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/batch/DataPoint.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/batch/DataPoint.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/batch/DataPoint.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/ProductLine.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/batchfetch/ProductLine.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/ProductLine.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/batchfetch/ProductLine.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bidi/Auction.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/bidi/Auction.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/bidi/Auction.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/bidi/Auction.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bidi/Auction2.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/bidi/Auction2.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/bidi/Auction2.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/bidi/Auction2.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bidi/Auction3.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/bidi/Auction3.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/bidi/Auction3.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/bidi/Auction3.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/cfgXml/badnamespace.cfg.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/boot/cfgXml/badnamespace.cfg.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/boot/cfgXml/badnamespace.cfg.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/boot/cfgXml/badnamespace.cfg.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/cfgXml/hibernate.cfg.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/boot/cfgXml/hibernate.cfg.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/boot/cfgXml/hibernate.cfg.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/boot/cfgXml/hibernate.cfg.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/binding/hbm/simple/dynamic/SimpleDynamicEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/bootstrap/binding/hbm/simple/dynamic/SimpleDynamicEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/binding/hbm/simple/dynamic/SimpleDynamicEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/bootstrap/binding/hbm/simple/dynamic/SimpleDynamicEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/binding/hbm/simple/pojo/SimpleEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/bootstrap/binding/hbm/simple/pojo/SimpleEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/binding/hbm/simple/pojo/SimpleEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/bootstrap/binding/hbm/simple/pojo/SimpleEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/Bean.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/bytecode/Bean.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/Bean.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/bytecode/Bean.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cascade/Child.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/Child.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cascade/Child.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/Child.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cascade/ChildForParentWithAssignedId.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/ChildForParentWithAssignedId.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cascade/ChildForParentWithAssignedId.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/ChildForParentWithAssignedId.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cascade/DeleteOrphanChild.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/DeleteOrphanChild.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cascade/DeleteOrphanChild.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/DeleteOrphanChild.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cascade/Job.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/Job.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cascade/Job.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/Job.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cascade/JobBatch.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/JobBatch.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cascade/JobBatch.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/JobBatch.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cascade/MultiPathCascade.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/MultiPathCascade.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cascade/MultiPathCascade.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/MultiPathCascade.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cascade/Parent.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/Parent.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cascade/Parent.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/Parent.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cascade/ParentWithAssignedId.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/ParentWithAssignedId.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cascade/ParentWithAssignedId.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/ParentWithAssignedId.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cascade/circle/CascadeManagedAndTransient.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/circle/CascadeManagedAndTransient.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cascade/circle/CascadeManagedAndTransient.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/circle/CascadeManagedAndTransient.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cascade/circle/CascadeMergeToChildBeforeParent.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/circle/CascadeMergeToChildBeforeParent.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cascade/circle/CascadeMergeToChildBeforeParent.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/circle/CascadeMergeToChildBeforeParent.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cascade/circle/MultiPathCircleCascade.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/circle/MultiPathCircleCascade.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cascade/circle/MultiPathCircleCascade.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/circle/MultiPathCircleCascade.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cascade/circle/MultiPathCircleCascadeDelayedInsert.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/circle/MultiPathCircleCascadeDelayedInsert.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cascade/circle/MultiPathCircleCascadeDelayedInsert.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cascade/circle/MultiPathCircleCascadeDelayedInsert.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cfg/Cacheable.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cfg/Cacheable.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cfg/Cacheable.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cfg/Cacheable.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cfg/FooEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cfg/FooEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cfg/FooEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cfg/FooEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cfg/cache/BaseClass.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cfg/cache/BaseClass.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cfg/cache/BaseClass.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cfg/cache/BaseClass.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cfg/cache/Item.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cfg/cache/Item.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cfg/cache/Item.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cfg/cache/Item.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cfg/cache/SubClass.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cfg/cache/SubClass.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cfg/cache/SubClass.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cfg/cache/SubClass.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cfg/cache/hibernate.cfg.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cfg/cache/hibernate.cfg.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cfg/cache/hibernate.cfg.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cfg/cache/hibernate.cfg.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cid/Customer.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cid/Customer.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cid/Customer.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cid/Customer.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cid/LineItem.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cid/LineItem.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cid/LineItem.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cid/LineItem.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cid/Order.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cid/Order.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cid/Order.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cid/Order.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cid/Product.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cid/Product.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cid/Product.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cid/Product.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cid/PurchaseRecord.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cid/PurchaseRecord.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cid/PurchaseRecord.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cid/PurchaseRecord.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/backref/map/compkey/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/collection/backref/map/compkey/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/collection/backref/map/compkey/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/collection/backref/map/compkey/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/bag/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/collection/bag/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/collection/bag/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/collection/bag/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/idbag/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/collection/idbag/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/collection/idbag/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/collection/idbag/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/lazynocascade/Parent.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/collection/lazynocascade/Parent.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/collection/lazynocascade/Parent.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/collection/lazynocascade/Parent.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/list/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/collection/list/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/collection/list/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/collection/list/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/list/ParentChildMapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/collection/list/ParentChildMapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/collection/list/ParentChildMapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/collection/list/ParentChildMapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/collection/map/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/collection/map/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/collection/map/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/original/UserPermissions.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/collection/original/UserPermissions.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/collection/original/UserPermissions.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/collection/original/UserPermissions.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/original/Zoo.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/collection/original/Zoo.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/collection/original/Zoo.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/collection/original/Zoo.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/propertyref/Mail.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/collection/propertyref/Mail.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/collection/propertyref/Mail.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/collection/propertyref/Mail.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/propertyref/User.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/collection/propertyref/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/collection/propertyref/User.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/collection/propertyref/User.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/propertyref/lazy/Mail.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/collection/propertyref/lazy/Mail.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/collection/propertyref/lazy/Mail.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/collection/propertyref/lazy/Mail.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/propertyref/lazy/User.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/collection/propertyref/lazy/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/collection/propertyref/lazy/User.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/collection/propertyref/lazy/User.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/set/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/collection/set/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/collection/set/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/collection/set/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/set/MappingsNonLazy.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/collection/set/MappingsNonLazy.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/collection/set/MappingsNonLazy.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/collection/set/MappingsNonLazy.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/component/basic/User.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/component/basic/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/component/basic/User.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/component/basic/User.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/component/cascading/collection/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/component/cascading/collection/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/component/cascading/collection/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/component/cascading/collection/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/component/cascading/toone/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/component/cascading/toone/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/component/cascading/toone/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/component/cascading/toone/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/compositeelement/Parent.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/compositeelement/Parent.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/compositeelement/Parent.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/compositeelement/Parent.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/connections/Silly.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/connections/Silly.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/connections/Silly.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/connections/Silly.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cuk/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cuk/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cuk/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cuk/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cut/Transaction.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/cut/Transaction.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/cut/Transaction.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/cut/Transaction.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/deletetransient/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/deletetransient/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/deletetransient/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/deletetransient/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/function/Product.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/dialect/function/Product.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/dialect/function/Product.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/dialect/function/Product.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dynamicmap/Test.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/dynamicmap/Test.hbm.xml similarity index 97% rename from hibernate-core/src/test/java/org/hibernate/orm/test/dynamicmap/Test.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/dynamicmap/Test.hbm.xml index 2005f06b65c2..1a5e764d90d8 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dynamicmap/Test.hbm.xml +++ b/hibernate-core/src/test/resources/org/hibernate/orm/test/dynamicmap/Test.hbm.xml @@ -1,58 +1,58 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ecid/Course.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/ecid/Course.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/ecid/Course.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/ecid/Course.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/named/basic/orm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/entitygraph/named/basic/orm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/named/basic/orm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/entitygraph/named/basic/orm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/named/subgraph/orm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/entitygraph/named/subgraph/orm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/named/subgraph/orm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/entitygraph/named/subgraph/orm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/xml/orm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/entitygraph/xml/orm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/xml/orm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/entitygraph/xml/orm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entitymode/map/basic/ProductLine.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/entitymode/map/basic/ProductLine.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/entitymode/map/basic/ProductLine.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/entitymode/map/basic/ProductLine.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entitymode/map/compositeId/CompId.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/entitymode/map/compositeId/CompId.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/entitymode/map/compositeId/CompId.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/entitymode/map/compositeId/CompId.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entitymode/map/subclass/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/entitymode/map/subclass/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/entitymode/map/subclass/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/entitymode/map/subclass/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entityname/MyEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/entityname/MyEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/entityname/MyEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/entityname/MyEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entityname/Vehicle.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/entityname/Vehicle.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/entityname/Vehicle.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/entityname/Vehicle.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/association/bidirectional/manytomany/BidirectionalManyToManyBagToSetMapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/association/bidirectional/manytomany/BidirectionalManyToManyBagToSetMapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/association/bidirectional/manytomany/BidirectionalManyToManyBagToSetMapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/association/bidirectional/manytomany/BidirectionalManyToManyBagToSetMapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/association/bidirectional/manytomany/BidirectionalManyToManySetToSetMapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/association/bidirectional/manytomany/BidirectionalManyToManySetToSetMapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/association/bidirectional/manytomany/BidirectionalManyToManySetToSetMapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/association/bidirectional/manytomany/BidirectionalManyToManySetToSetMapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/association/bidirectional/onetomany/BidirectionalOneToManyBagMapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/association/bidirectional/onetomany/BidirectionalOneToManyBagMapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/association/bidirectional/onetomany/BidirectionalOneToManyBagMapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/association/bidirectional/onetomany/BidirectionalOneToManyBagMapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/association/bidirectional/onetomany/BidirectionalOneToManyBagSubclassMapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/association/bidirectional/onetomany/BidirectionalOneToManyBagSubclassMapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/association/bidirectional/onetomany/BidirectionalOneToManyBagSubclassMapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/association/bidirectional/onetomany/BidirectionalOneToManyBagSubclassMapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/association/bidirectional/onetomany/BidirectionalOneToManySetMapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/association/bidirectional/onetomany/BidirectionalOneToManySetMapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/association/bidirectional/onetomany/BidirectionalOneToManySetMapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/association/bidirectional/onetomany/BidirectionalOneToManySetMapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/association/unidirectional/manytomany/UnidirectionalManyToManyBagMapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/association/unidirectional/manytomany/UnidirectionalManyToManyBagMapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/association/unidirectional/manytomany/UnidirectionalManyToManyBagMapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/association/unidirectional/manytomany/UnidirectionalManyToManyBagMapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/association/unidirectional/onetomany/UnidirectionalOneToManyBagMapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/association/unidirectional/onetomany/UnidirectionalOneToManyBagMapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/association/unidirectional/onetomany/UnidirectionalOneToManyBagMapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/association/unidirectional/onetomany/UnidirectionalOneToManyBagMapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/association/unidirectional/onetomany/UnidirectionalOneToManySetMapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/association/unidirectional/onetomany/UnidirectionalOneToManySetMapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/association/unidirectional/onetomany/UnidirectionalOneToManySetMapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/association/unidirectional/onetomany/UnidirectionalOneToManySetMapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/detached/MultipleCollectionBagMapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/detached/MultipleCollectionBagMapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/detached/MultipleCollectionBagMapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/detached/MultipleCollectionBagMapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/values/ValuesBagMapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/values/ValuesBagMapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/event/collection/values/ValuesBagMapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/event/collection/values/ValuesBagMapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/events/DefaultEntityListener-orm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/events/DefaultEntityListener-orm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/events/DefaultEntityListener-orm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/events/DefaultEntityListener-orm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/exception/Group.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/exception/Group.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/exception/Group.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/exception/Group.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/exception/User.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/exception/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/exception/User.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/exception/User.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/extendshbm/Customer.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/extendshbm/Customer.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/extendshbm/Customer.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/extendshbm/Customer.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/extendshbm/Employee.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/extendshbm/Employee.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/extendshbm/Employee.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/extendshbm/Employee.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/extendshbm/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/extendshbm/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/extendshbm/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/extendshbm/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/extendshbm/allinone.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/extendshbm/allinone.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/extendshbm/allinone.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/extendshbm/allinone.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/extendshbm/allseparateinone.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/extendshbm/allseparateinone.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/extendshbm/allseparateinone.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/extendshbm/allseparateinone.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/extendshbm/entitynames.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/extendshbm/entitynames.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/extendshbm/entitynames.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/extendshbm/entitynames.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/extendshbm/packageentitynames.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/extendshbm/packageentitynames.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/extendshbm/packageentitynames.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/extendshbm/packageentitynames.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/extendshbm/unionsubclass.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/extendshbm/unionsubclass.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/extendshbm/unionsubclass.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/extendshbm/unionsubclass.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/extralazy/Child.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/extralazy/Child.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/extralazy/Child.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/extralazy/Child.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/extralazy/Parent.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/extralazy/Parent.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/extralazy/Parent.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/extralazy/Parent.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/extralazy/UserGroup.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/extralazy/UserGroup.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/extralazy/UserGroup.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/extralazy/UserGroup.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/fetchprofiles/join/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/fetchprofiles/join/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/fetchprofiles/join/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/fetchprofiles/join/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/filter/Category.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/filter/Category.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/filter/Category.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/filter/Category.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/filter/Department.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/filter/Department.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/filter/Department.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/filter/Department.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/filter/LineItem.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/filter/LineItem.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/filter/LineItem.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/filter/LineItem.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/filter/Order.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/filter/Order.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/filter/Order.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/filter/Order.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/filter/Product.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/filter/Product.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/filter/Product.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/filter/Product.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/filter/Salesperson.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/filter/Salesperson.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/filter/Salesperson.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/filter/Salesperson.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/filter/defs.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/filter/defs.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/filter/defs.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/filter/defs.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/formulajoin/Root.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/formulajoin/Root.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/formulajoin/Root.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/formulajoin/Root.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/generatedkeys/generated/MyEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/generatedkeys/generated/MyEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/generatedkeys/generated/MyEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/generatedkeys/generated/MyEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/generatedkeys/identity/MyEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/generatedkeys/identity/MyEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/generatedkeys/identity/MyEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/generatedkeys/identity/MyEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/generatedkeys/select/MyEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/generatedkeys/select/MyEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/generatedkeys/select/MyEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/generatedkeys/select/MyEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/generatedkeys/selectannotated/MyEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/generatedkeys/selectannotated/MyEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/generatedkeys/selectannotated/MyEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/generatedkeys/selectannotated/MyEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/inheritance/AnimalReport.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hbm/inheritance/AnimalReport.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hbm/inheritance/AnimalReport.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hbm/inheritance/AnimalReport.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/mappingexception/unmapped_association.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hbm/mappingexception/unmapped_association.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hbm/mappingexception/unmapped_association.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hbm/mappingexception/unmapped_association.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/mappingexception/unmapped_collection.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hbm/mappingexception/unmapped_collection.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hbm/mappingexception/unmapped_collection.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hbm/mappingexception/unmapped_collection.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.orm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.orm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.orm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.orm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/Animal.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hql/Animal.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hql/Animal.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hql/Animal.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/BooleanLiteralEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hql/BooleanLiteralEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hql/BooleanLiteralEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hql/BooleanLiteralEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ComponentContainer.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hql/ComponentContainer.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hql/ComponentContainer.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hql/ComponentContainer.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/CompositeIdEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hql/CompositeIdEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hql/CompositeIdEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hql/CompositeIdEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/Constructor.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hql/Constructor.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hql/Constructor.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hql/Constructor.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/CrazyIdFieldNames.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hql/CrazyIdFieldNames.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hql/CrazyIdFieldNames.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hql/CrazyIdFieldNames.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/EntityWithCrazyCompositeKey.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hql/EntityWithCrazyCompositeKey.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hql/EntityWithCrazyCompositeKey.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hql/EntityWithCrazyCompositeKey.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/FooBarCopy.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hql/FooBarCopy.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hql/FooBarCopy.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hql/FooBarCopy.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/FunctionNamesAsColumns.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hql/FunctionNamesAsColumns.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hql/FunctionNamesAsColumns.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hql/FunctionNamesAsColumns.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/Image.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hql/Image.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hql/Image.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hql/Image.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/KeyManyToOneEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hql/KeyManyToOneEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hql/KeyManyToOneEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hql/KeyManyToOneEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/Properties.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hql/Properties.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hql/Properties.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hql/Properties.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/SimpleEntityWithAssociation.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hql/SimpleEntityWithAssociation.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hql/SimpleEntityWithAssociation.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hql/SimpleEntityWithAssociation.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/VariousKeywordPropertyEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hql/VariousKeywordPropertyEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hql/VariousKeywordPropertyEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hql/VariousKeywordPropertyEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/Vehicle.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hql/Vehicle.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hql/Vehicle.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hql/Vehicle.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/Versions.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hql/Versions.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hql/Versions.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hql/Versions.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hqlfetchscroll/ParentChild.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/hqlfetchscroll/ParentChild.hbm.xml similarity index 96% rename from hibernate-core/src/test/java/org/hibernate/orm/test/hqlfetchscroll/ParentChild.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/hqlfetchscroll/ParentChild.hbm.xml index 69de821b9556..9021a52d2dff 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/hqlfetchscroll/ParentChild.hbm.xml +++ b/hibernate-core/src/test/resources/org/hibernate/orm/test/hqlfetchscroll/ParentChild.hbm.xml @@ -1,41 +1,41 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/id/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/id/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/id/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/Product.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/id/Product.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/id/Product.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/id/Product.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/SQLServer2012Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/id/SQLServer2012Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/id/SQLServer2012Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/id/SQLServer2012Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idbag/UserGroup.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/idbag/UserGroup.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/idbag/UserGroup.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/idbag/UserGroup.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/Customer.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/idclass/Customer.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/idclass/Customer.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/idclass/Customer.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/biginteger/increment/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/biginteger/increment/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/idgen/biginteger/increment/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/biginteger/increment/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/biginteger/sequence/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/biginteger/sequence/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/idgen/biginteger/sequence/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/biginteger/sequence/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/biginteger/sequence/ZeroScaleMapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/biginteger/sequence/ZeroScaleMapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/idgen/biginteger/sequence/ZeroScaleMapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/biginteger/sequence/ZeroScaleMapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/forcedtable/Basic.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/forcedtable/Basic.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/forcedtable/Basic.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/forcedtable/Basic.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/forcedtable/HiLo.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/forcedtable/HiLo.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/forcedtable/HiLo.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/forcedtable/HiLo.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/forcedtable/Pooled.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/forcedtable/Pooled.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/forcedtable/Pooled.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/forcedtable/Pooled.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/sequence/Basic.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/sequence/Basic.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/sequence/Basic.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/sequence/Basic.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/sequence/Dedicated.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/sequence/Dedicated.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/sequence/Dedicated.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/sequence/Dedicated.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/sequence/HiLo.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/sequence/HiLo.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/sequence/HiLo.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/sequence/HiLo.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/sequence/Pooled.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/sequence/Pooled.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/sequence/Pooled.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/sequence/Pooled.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/table/Basic.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/table/Basic.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/table/Basic.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/table/Basic.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/table/HiLo.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/table/HiLo.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/table/HiLo.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/table/HiLo.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/table/Pooled.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/table/Pooled.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/table/Pooled.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/idgen/enhanced/table/Pooled.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idprops/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/idprops/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/idprops/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/idprops/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/immutable/ContractVariation.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/ContractVariation.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/immutable/ContractVariation.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/ContractVariation.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariation.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariation.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariation.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariation.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariationOneToManyJoin.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariationOneToManyJoin.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariationOneToManyJoin.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariationOneToManyJoin.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariationVersioned.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariationVersioned.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariationVersioned.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariationVersioned.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariationVersionedOneToManyJoin.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariationVersionedOneToManyJoin.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariationVersionedOneToManyJoin.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/inverse/ContractVariationVersionedOneToManyJoin.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariation.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariation.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariation.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariation.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationOneToManyJoin.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationOneToManyJoin.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationOneToManyJoin.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationOneToManyJoin.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationUnidir.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationUnidir.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationUnidir.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationUnidir.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationVersioned.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationVersioned.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationVersioned.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationVersioned.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationVersionedOneToManyJoin.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationVersionedOneToManyJoin.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationVersionedOneToManyJoin.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/immutable/entitywithmutablecollection/noninverse/ContractVariationVersionedOneToManyJoin.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/joinedsubclass/TestEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/inheritance/discriminator/joinedsubclass/TestEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/joinedsubclass/TestEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/inheritance/discriminator/joinedsubclass/TestEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/insertordering/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/insertordering/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/insertordering/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/insertordering/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/interceptor/Image.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/interceptor/Image.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/interceptor/Image.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/interceptor/Image.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/interceptor/User.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/interceptor/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/interceptor/User.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/interceptor/User.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/interfaceproxy/Item.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/interfaceproxy/Item.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/interfaceproxy/Item.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/interfaceproxy/Item.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/iterate/Item.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/iterate/Item.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/iterate/Item.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/iterate/Item.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jdbc/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/jdbc/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/jdbc/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/jdbc/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/join/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/join/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/join/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/join/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/join/Reportable.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/join/Reportable.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/join/Reportable.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/join/Reportable.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/join/Thing.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/join/Thing.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/join/Thing.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/join/Thing.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/joinedsubclass/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/joinedsubclass/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/joinedsubclass/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/joinedsubclass/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/cascade2/ParentChild.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/jpa/cascade2/ParentChild.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/jpa/cascade2/ParentChild.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/jpa/cascade2/ParentChild.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listener/orm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/jpa/compliance/callback/listener/orm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/listener/orm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/jpa/compliance/callback/listener/orm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/fetch/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/jpa/fetch/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/jpa/fetch/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/jpa/fetch/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/model/Item.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/jpa/model/Item.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/jpa/model/Item.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/jpa/model/Item.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/model/MyEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/jpa/model/MyEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/jpa/model/MyEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/jpa/model/MyEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/model/Part.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/jpa/model/Part.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/jpa/model/Part.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/jpa/model/Part.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/naturalid/User.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/jpa/naturalid/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/jpa/naturalid/User.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/jpa/naturalid/User.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/keymanytoone/bidir/component/EagerCollectionLazyKeyManyToOneMapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/keymanytoone/bidir/component/EagerCollectionLazyKeyManyToOneMapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/keymanytoone/bidir/component/EagerCollectionLazyKeyManyToOneMapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/keymanytoone/bidir/component/EagerCollectionLazyKeyManyToOneMapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/keymanytoone/bidir/component/EagerMapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/keymanytoone/bidir/component/EagerMapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/keymanytoone/bidir/component/EagerMapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/keymanytoone/bidir/component/EagerMapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/keymanytoone/bidir/component/LazyMapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/keymanytoone/bidir/component/LazyMapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/keymanytoone/bidir/component/LazyMapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/keymanytoone/bidir/component/LazyMapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/keymanytoone/bidir/embedded/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/keymanytoone/bidir/embedded/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/keymanytoone/bidir/embedded/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/keymanytoone/bidir/embedded/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/keymanytoone/bidir/ondelete/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/keymanytoone/bidir/ondelete/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/keymanytoone/bidir/ondelete/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/keymanytoone/bidir/ondelete/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/keymanytoone/unidir/ondelete/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/keymanytoone/unidir/ondelete/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/keymanytoone/unidir/ondelete/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/keymanytoone/unidir/ondelete/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/lazyonetoone/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/lazyonetoone/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/lazyonetoone/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/lazyonetoone/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/ABC.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/ABC.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/ABC.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/ABC.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/ABCExtends.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/ABCExtends.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/ABCExtends.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/ABCExtends.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/ABCProxy.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/ABCProxy.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/ABCProxy.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/ABCProxy.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/AltSimple.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/AltSimple.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/AltSimple.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/AltSimple.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Baz.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Baz.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Baz.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Baz.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Blobber.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Blobber.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Blobber.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Blobber.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Broken.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Broken.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Broken.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Broken.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Category.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Category.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Category.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Category.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Circular.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Circular.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Circular.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Circular.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Commento.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Commento.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Commento.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Commento.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/ComponentNotNullRoot.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/ComponentNotNullRoot.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/ComponentNotNullRoot.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/ComponentNotNullRoot.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Componentizable.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Componentizable.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Componentizable.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Componentizable.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/CompositeIdId.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/CompositeIdId.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/CompositeIdId.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/CompositeIdId.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Container.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Container.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Container.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Container.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Custom.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Custom.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Custom.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Custom.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/CustomSQL.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/CustomSQL.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/CustomSQL.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/CustomSQL.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Eye.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Eye.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Eye.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Eye.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Fee.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Fee.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Fee.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Fee.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Fo.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Fo.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Fo.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Fo.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/FooBar.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/FooBar.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/FooBar.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/FooBar.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Fum.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Fum.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Fum.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Fum.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Fumm.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Fumm.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Fumm.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Fumm.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Glarch.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Glarch.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Glarch.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Glarch.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Holder.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Holder.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Holder.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Holder.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/IJ.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/IJ.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/IJ.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/IJ.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/IJ2.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/IJ2.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/IJ2.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/IJ2.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Immutable.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Immutable.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Immutable.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Immutable.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Location.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Location.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Location.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Location.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/MainObject.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/MainObject.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/MainObject.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/MainObject.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Many.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Many.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Many.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Many.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Map.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Map.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Map.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Map.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Marelo.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Marelo.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Marelo.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Marelo.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Middle.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Middle.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Middle.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Middle.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Multi.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Multi.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Multi.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Multi.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/MultiExtends.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/MultiExtends.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/MultiExtends.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/MultiExtends.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Nameable.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Nameable.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Nameable.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Nameable.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Object2.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Object2.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Object2.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Object2.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/One.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/One.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/One.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/One.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/ParentChild.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/ParentChild.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/ParentChild.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/ParentChild.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Qux.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Qux.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Qux.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Qux.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/RootDetail.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/RootDetail.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/RootDetail.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/RootDetail.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Simple.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Simple.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Simple.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Simple.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/SingleSeveral.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/SingleSeveral.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/SingleSeveral.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/SingleSeveral.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Stuff.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Stuff.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Stuff.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Stuff.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/UpDown.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/UpDown.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/UpDown.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/UpDown.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Vetoer.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Vetoer.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Vetoer.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Vetoer.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/WZ.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/WZ.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/WZ.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/WZ.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Wicked.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Wicked.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/Wicked.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/Wicked.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/XY.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/XY.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/legacy/XY.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/legacy/XY.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/ImageMappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/lob/ImageMappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/lob/ImageMappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/lob/ImageMappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobAsLastValue.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/lob/LobAsLastValue.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobAsLastValue.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/lob/LobAsLastValue.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobMappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/lob/LobMappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobMappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/lob/LobMappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/MaterializedBlobMappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/lob/MaterializedBlobMappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/lob/MaterializedBlobMappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/lob/MaterializedBlobMappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/MaterializedClobMappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/lob/MaterializedClobMappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/lob/MaterializedClobMappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/lob/MaterializedClobMappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/SerializableMappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/lob/SerializableMappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/lob/SerializableMappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/lob/SerializableMappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/lob/TextMappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/lob/TextMappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/lob/TextMappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/lob/TextMappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/manytomany/UserGroup.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/manytomany/UserGroup.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/manytomany/UserGroup.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/manytomany/UserGroup.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/manytomany/batchload/UserGroupBatchLoad.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/manytomany/batchload/UserGroupBatchLoad.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/manytomany/batchload/UserGroupBatchLoad.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/manytomany/batchload/UserGroupBatchLoad.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/manytomany/ordered/UserGroup.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/manytomany/ordered/UserGroup.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/manytomany/ordered/UserGroup.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/manytomany/ordered/UserGroup.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/manytomanyassociationclass/compositeid/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/manytomanyassociationclass/compositeid/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/manytomanyassociationclass/compositeid/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/manytomanyassociationclass/compositeid/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/manytomanyassociationclass/nestedreference/Item.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/manytomanyassociationclass/nestedreference/Item.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/manytomanyassociationclass/nestedreference/Item.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/manytomanyassociationclass/nestedreference/Item.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/manytomanyassociationclass/surrogateid/assigned/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/manytomanyassociationclass/surrogateid/assigned/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/manytomanyassociationclass/surrogateid/assigned/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/manytomanyassociationclass/surrogateid/assigned/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/manytomanyassociationclass/surrogateid/generated/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/manytomanyassociationclass/surrogateid/generated/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/manytomanyassociationclass/surrogateid/generated/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/manytomanyassociationclass/surrogateid/generated/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/map/UserGroup.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/map/UserGroup.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/map/UserGroup.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/map/UserGroup.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapelemformula/UserGroup.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapelemformula/UserGroup.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapelemformula/UserGroup.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapelemformula/UserGroup.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/attrorder/mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/attrorder/mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/attrorder/mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/attrorder/mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/custom/basic/UserPermissions.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/collections/custom/basic/UserPermissions.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/custom/basic/UserPermissions.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/collections/custom/basic/UserPermissions.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/custom/declaredtype/UserPermissions.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/collections/custom/declaredtype/UserPermissions.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/custom/declaredtype/UserPermissions.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/collections/custom/declaredtype/UserPermissions.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/custom/parameterized/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/collections/custom/parameterized/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/custom/parameterized/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/collections/custom/parameterized/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/mapcompelem/ProductPart.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/collections/mapcompelem/ProductPart.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/mapcompelem/ProductPart.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/collections/mapcompelem/ProductPart.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/contributed/BasicContributorTests.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/contributed/BasicContributorTests.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/contributed/BasicContributorTests.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/contributed/BasicContributorTests.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/formula/EntityOfFormulas.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/formula/EntityOfFormulas.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/formula/EntityOfFormulas.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/formula/EntityOfFormulas.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/ComponentOwner.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/generated/ComponentOwner.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/ComponentOwner.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/generated/ComponentOwner.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/GeneratedPropertyEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/generated/GeneratedPropertyEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/GeneratedPropertyEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/generated/GeneratedPropertyEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/MSSQLGeneratedPropertyEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/generated/MSSQLGeneratedPropertyEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/MSSQLGeneratedPropertyEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/generated/MSSQLGeneratedPropertyEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/discriminator/AccountOwner.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/inheritance/discriminator/AccountOwner.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/discriminator/AccountOwner.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/inheritance/discriminator/AccountOwner.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/discriminator/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/inheritance/discriminator/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/discriminator/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/inheritance/discriminator/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/discriminator/SimpleInheritance.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/inheritance/discriminator/SimpleInheritance.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/discriminator/SimpleInheritance.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/inheritance/discriminator/SimpleInheritance.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/discriminator/joined/JoinedSubclassInheritance.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/inheritance/discriminator/joined/JoinedSubclassInheritance.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/discriminator/joined/JoinedSubclassInheritance.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/inheritance/discriminator/joined/JoinedSubclassInheritance.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/dynamic/JoinedMappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/inheritance/dynamic/JoinedMappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/dynamic/JoinedMappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/inheritance/dynamic/JoinedMappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/joined/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/inheritance/joined/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/joined/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/inheritance/joined/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/Account.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/cid/Account.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/Account.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/cid/Account.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/ParentChildWithManyToOne.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/immutable/ParentChildWithManyToOne.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/ParentChildWithManyToOne.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/immutable/ParentChildWithManyToOne.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/User.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/immutable/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/User.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/immutable/User.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/User.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/mutable/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/User.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/mutable/User.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/nullable/User.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/nullable/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/nullable/User.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/nullable/User.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/readwrite/ReadWriteEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/readwrite/ReadWriteEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/readwrite/ReadWriteEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/readwrite/ReadWriteEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/typedmanytoone/Customer.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/typedmanytoone/Customer.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/typedmanytoone/Customer.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/typedmanytoone/Customer.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/usertypes/TestEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/usertypes/TestEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/usertypes/TestEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/usertypes/TestEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/usertypes/TestEnumType.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/usertypes/TestEnumType.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/usertypes/TestEnumType.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/usertypes/TestEnumType.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/usertypes/xmlmapping/entities.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/usertypes/xmlmapping/entities.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/usertypes/xmlmapping/entities.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/usertypes/xmlmapping/entities.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mappingexception/InvalidMapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mappingexception/InvalidMapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mappingexception/InvalidMapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mappingexception/InvalidMapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mappingexception/User.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mappingexception/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mappingexception/User.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mappingexception/User.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/math/Math.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/math/Math.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/math/Math.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/math/Math.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mixed/Item.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/mixed/Item.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mixed/Item.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/mixed/Item.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/namingstrategy/Customers.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/namingstrategy/Customers.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/namingstrategy/Customers.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/namingstrategy/Customers.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/namingstrategy/complete/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/namingstrategy/complete/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/namingstrategy/complete/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/namingstrategy/complete/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/nonpkassociation/NonPkManyToOneAssociationHbmTest.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/nonpkassociation/NonPkManyToOneAssociationHbmTest.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/nonpkassociation/NonPkManyToOneAssociationHbmTest.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/nonpkassociation/NonPkManyToOneAssociationHbmTest.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ondelete/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/ondelete/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/ondelete/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/ondelete/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ondelete/toone/hbm/ToOneOnDelete.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/ondelete/toone/hbm/ToOneOnDelete.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/ondelete/toone/hbm/ToOneOnDelete.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/ondelete/toone/hbm/ToOneOnDelete.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/onetomany/Node.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/onetomany/Node.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/onetomany/Node.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/onetomany/Node.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/onetomany/Parent.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/onetomany/Parent.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/onetomany/Parent.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/onetomany/Parent.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/onetomany/VersionedNode.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/onetomany/VersionedNode.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/onetomany/VersionedNode.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/onetomany/VersionedNode.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/cache/Details.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/cache/Details.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/cache/Details.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/cache/Details.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/cache/MainObject.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/cache/MainObject.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/cache/MainObject.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/cache/MainObject.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/cache/Object2.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/cache/Object2.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/cache/Object2.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/cache/Object2.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/cache/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/cache/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/cache/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/cache/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/formula/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/formula/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/formula/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/formula/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/joined/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/joined/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/joined/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/joined/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/link/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/link/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/link/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/link/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/nopojo/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/nopojo/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/nopojo/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/nopojo/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/optional/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/optional/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/optional/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/optional/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/singletable/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/singletable/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/onetoone/singletable/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/onetoone/singletable/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ops/Competition.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/ops/Competition.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/ops/Competition.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/ops/Competition.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ops/Employer.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/ops/Employer.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/ops/Employer.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/ops/Employer.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ops/Hoarder.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/ops/Hoarder.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/ops/Hoarder.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/ops/Hoarder.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ops/HoarderOrphanDelete.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/ops/HoarderOrphanDelete.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/ops/HoarderOrphanDelete.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/ops/HoarderOrphanDelete.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ops/Node.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/ops/Node.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/ops/Node.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/ops/Node.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ops/OneToOne.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/ops/OneToOne.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/ops/OneToOne.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/ops/OneToOne.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ops/OptLockEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/ops/OptLockEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/ops/OptLockEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/ops/OptLockEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ops/SimpleEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/ops/SimpleEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/ops/SimpleEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/ops/SimpleEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/optlock/Document.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/optlock/Document.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/optlock/Document.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/optlock/Document.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ordered/Search.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/ordered/Search.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/ordered/Search.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/ordered/Search.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/orphan/Mail.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/Mail.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/orphan/Mail.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/Mail.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/orphan/Product.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/Product.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/orphan/Product.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/Product.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/orphan/User.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/orphan/User.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/User.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/orphan/elementcollection/student.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/elementcollection/student.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/orphan/elementcollection/student.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/elementcollection/student.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/orphan/manytomany/UserGroup.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/manytomany/UserGroup.hbm.xml similarity index 96% rename from hibernate-core/src/test/java/org/hibernate/orm/test/orphan/manytomany/UserGroup.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/manytomany/UserGroup.hbm.xml index 601373499458..748d4e0d5029 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/orphan/manytomany/UserGroup.hbm.xml +++ b/hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/manytomany/UserGroup.hbm.xml @@ -1,31 +1,31 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/orphan/one2one/fk/bidirectional/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/one2one/fk/bidirectional/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/orphan/one2one/fk/bidirectional/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/one2one/fk/bidirectional/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/orphan/one2one/fk/composite/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/one2one/fk/composite/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/orphan/one2one/fk/composite/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/one2one/fk/composite/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/orphan/one2one/fk/reversed/bidirectional/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/one2one/fk/reversed/bidirectional/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/orphan/one2one/fk/reversed/bidirectional/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/one2one/fk/reversed/bidirectional/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/orphan/one2one/fk/reversed/unidirectional/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/one2one/fk/reversed/unidirectional/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/orphan/one2one/fk/reversed/unidirectional/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/one2one/fk/reversed/unidirectional/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/orphan/one2one/pk/bidirectional/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/one2one/pk/bidirectional/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/orphan/one2one/pk/bidirectional/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/one2one/pk/bidirectional/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/orphan/one2one/pk/unidirectional/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/one2one/pk/unidirectional/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/orphan/one2one/pk/unidirectional/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/orphan/one2one/pk/unidirectional/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/basic/EntityClass.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/basic/EntityClass.hbm.xml similarity index 97% rename from hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/basic/EntityClass.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/basic/EntityClass.hbm.xml index fb2fbdb44012..957053cccf22 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/basic/EntityClass.hbm.xml +++ b/hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/basic/EntityClass.hbm.xml @@ -1,27 +1,27 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/basic/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/basic/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/basic/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/basic/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/cachedcollections/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/cachedcollections/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/cachedcollections/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/cachedcollections/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/component/complete/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/component/complete/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/component/complete/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/component/complete/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/inheritence/discrim/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/inheritence/discrim/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/inheritence/discrim/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/inheritence/discrim/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/inheritence/joined/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/inheritence/joined/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/inheritence/joined/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/inheritence/joined/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/inheritence/union/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/inheritence/union/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/inheritence/union/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/inheritence/union/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/partial/Mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/partial/Mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/partial/Mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/partial/Mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/DataPoint.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/proxy/DataPoint.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/proxy/DataPoint.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/proxy/DataPoint.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/joinfetch/ItemBid.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/query/joinfetch/ItemBid.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/query/joinfetch/ItemBid.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/query/joinfetch/ItemBid.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/joinfetch/UserGroup.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/query/joinfetch/UserGroup.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/query/joinfetch/UserGroup.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/query/joinfetch/UserGroup.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/result-set-mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/query/resultmapping/result-set-mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/result-set-mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/query/resultmapping/result-set-mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/querycache/Enrolment.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/querycache/Enrolment.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/querycache/Enrolment.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/querycache/Enrolment.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/querycache/Item.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/querycache/Item.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/querycache/Item.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/querycache/Item.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/quote/DataPoint.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/quote/DataPoint.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/quote/DataPoint.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/quote/DataPoint.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/readonly/DataPoint.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/readonly/DataPoint.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/readonly/DataPoint.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/readonly/DataPoint.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/readonly/Enrolment.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/readonly/Enrolment.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/readonly/Enrolment.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/readonly/Enrolment.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/readonly/TextHolder.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/readonly/TextHolder.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/readonly/TextHolder.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/readonly/TextHolder.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/readonly/VersionedNode.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/readonly/VersionedNode.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/readonly/VersionedNode.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/readonly/VersionedNode.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/reattachment/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/reattachment/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/reattachment/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/reattachment/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/refresh/Customer.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/refresh/Customer.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/refresh/Customer.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/refresh/Customer.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/resulttransformer/Contract.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/resulttransformer/Contract.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/resulttransformer/Contract.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/resulttransformer/Contract.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/rowid/Point.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/rowid/Point.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/rowid/Point.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/rowid/Point.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schema/SchemaGenerationTest.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schema/SchemaGenerationTest.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schema/SchemaGenerationTest.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schema/SchemaGenerationTest.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/1_Version.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/1_Version.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/1_Version.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/1_Version.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/2_Version.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/2_Version.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/2_Version.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/2_Version.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/3_Version.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/3_Version.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/3_Version.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/3_Version.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/4_Version.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/4_Version.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/4_Version.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/4_Version.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/CommentGeneration.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/CommentGeneration.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/CommentGeneration.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/CommentGeneration.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/UserGroup.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/UserGroup.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/UserGroup.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/UserGroup.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/idbag/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/idbag/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/idbag/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/idbag/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/idgenerator/sequence.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/idgenerator/sequence.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/idgenerator/sequence.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/idgenerator/sequence.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/inheritance/Employee.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/inheritance/Employee.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/inheritance/Employee.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/inheritance/Employee.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/inheritance/Manager.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/inheritance/Manager.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/inheritance/Manager.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/inheritance/Manager.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/inheritance/Payment.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/inheritance/Payment.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/inheritance/Payment.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/inheritance/Payment.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/inheritance/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/inheritance/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/inheritance/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/inheritance/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/manytomany/UserGroup.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/manytomany/UserGroup.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/manytomany/UserGroup.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/manytomany/UserGroup.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/mapping2.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/mapping2.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/mapping2.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/mapping2.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/TestEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/uniqueconstraint/TestEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/TestEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemaupdate/uniqueconstraint/TestEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/check/oracle-mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/sql/check/oracle-mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/sql/check/oracle-mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/sql/check/oracle-mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/custom/datadirect/oracle/StoredProcedures.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/custom/datadirect/oracle/StoredProcedures.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/custom/datadirect/oracle/StoredProcedures.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/custom/datadirect/oracle/StoredProcedures.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/custom/db2/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/custom/db2/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/custom/db2/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/custom/db2/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/custom/derby/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/custom/derby/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/custom/derby/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/custom/derby/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/custom/mysql/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/custom/mysql/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/custom/mysql/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/custom/mysql/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/custom/oracle/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/custom/oracle/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/custom/oracle/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/custom/oracle/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/custom/oracle/StoredProcedures.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/custom/oracle/StoredProcedures.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/custom/oracle/StoredProcedures.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/custom/oracle/StoredProcedures.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/custom/sqlserver/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/custom/sqlserver/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/custom/sqlserver/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/custom/sqlserver/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/custom/sybase/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/custom/sybase/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/custom/sybase/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/custom/sybase/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/identity/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/identity/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/identity/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/identity/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/query/NativeSQLQueries.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/query/NativeSQLQueries.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/query/NativeSQLQueries.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/query/NativeSQLQueries.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/quotedidentifiers/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/quotedidentifiers/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/quotedidentifiers/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/sql/hand/quotedidentifiers/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/Contact.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/stateless/Contact.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/stateless/Contact.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/stateless/Contact.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/Document.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/stateless/Document.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/stateless/Document.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/stateless/Document.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/fetching/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/stateless/fetching/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/stateless/fetching/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/stateless/fetching/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/insert/Mappings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/stateless/insert/Mappings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/stateless/insert/Mappings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/stateless/insert/Mappings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/stats/Continent.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/stats/Continent.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/stats/Continent.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/stats/Continent.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/stats/Continent2.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/stats/Continent2.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/stats/Continent2.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/stats/Continent2.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/subclassProxyInterface/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/subclassProxyInterface/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/subclassProxyInterface/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/subclassProxyInterface/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/subclassfilter/discrim-subclass.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/subclassfilter/discrim-subclass.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/subclassfilter/discrim-subclass.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/subclassfilter/discrim-subclass.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/subclassfilter/joined-subclass.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/subclassfilter/joined-subclass.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/subclassfilter/joined-subclass.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/subclassfilter/joined-subclass.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/subclassfilter/union-subclass.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/subclassfilter/union-subclass.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/subclassfilter/union-subclass.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/subclassfilter/union-subclass.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/subselect/Beings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/subselect/Beings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/subselect/Beings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/subselect/Beings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/subselect/Book.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/subselect/Book.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/subselect/Book.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/subselect/Book.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/subselect/join/Order.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/subselect/join/Order.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/subselect/join/Order.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/subselect/join/Order.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ternary/Ternary.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/ternary/Ternary.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/ternary/Ternary.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/ternary/Ternary.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/timestamp/User.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/timestamp/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/timestamp/User.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/timestamp/User.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/tm/Item.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/tm/Item.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/tm/Item.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/tm/Item.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/typedonetoone/Customer.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/typedonetoone/Customer.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/typedonetoone/Customer.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/typedonetoone/Customer.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/typeoverride/Entity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/typeoverride/Entity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/typeoverride/Entity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/typeoverride/Entity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/typeparameters/Typedef.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/typeparameters/Typedef.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/typeparameters/Typedef.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/typeparameters/Typedef.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/typeparameters/Widget.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/typeparameters/Widget.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/typeparameters/Widget.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/typeparameters/Widget.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/unconstrained/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/unconstrained/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/unconstrained/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/unconstrained/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/unidir/ParentChild.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/unidir/ParentChild.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/unidir/ParentChild.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/unidir/ParentChild.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/unidir/ParentChildPropertyRef.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/unidir/ParentChildPropertyRef.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/unidir/ParentChildPropertyRef.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/unidir/ParentChildPropertyRef.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/unionsubclass/Beings.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/unionsubclass/Beings.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/unionsubclass/Beings.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/unionsubclass/Beings.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/unionsubclass/alias/mapping.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/unionsubclass/alias/mapping.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/unionsubclass/alias/mapping.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/unionsubclass/alias/mapping.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/unionsubclass2/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/unionsubclass2/Person.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/unionsubclass2/Person.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/unionsubclass2/Person.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/util/dtd/Parent.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/util/dtd/Parent.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/util/dtd/Parent.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/util/dtd/Parent.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/util/dtd/child.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/util/dtd/child.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/util/dtd/child.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/util/dtd/child.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/version/PersonThing.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/version/PersonThing.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/version/PersonThing.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/version/PersonThing.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/version/db/User.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/version/db/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/version/db/User.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/version/db/User.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/version/mappedsuperclass/TestEntity.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/version/mappedsuperclass/TestEntity.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/version/mappedsuperclass/TestEntity.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/version/mappedsuperclass/TestEntity.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/version/sybase/User.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/version/sybase/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/version/sybase/User.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/version/sybase/User.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/EagerManyToOneFetchModeJoinWhereTest.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/EagerManyToOneFetchModeJoinWhereTest.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/EagerManyToOneFetchModeJoinWhereTest.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/EagerManyToOneFetchModeJoinWhereTest.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/EagerManyToOneFetchModeSelectWhereTest.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/EagerManyToOneFetchModeSelectWhereTest.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/EagerManyToOneFetchModeSelectWhereTest.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/EagerManyToOneFetchModeSelectWhereTest.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/EagerToManyWhere.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/EagerToManyWhere.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/EagerToManyWhere.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/EagerToManyWhere.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/File.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/File.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/File.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/File.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/LazyElementCollectionBasicNonUniqueIdWhereTest.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/LazyElementCollectionBasicNonUniqueIdWhereTest.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/LazyElementCollectionBasicNonUniqueIdWhereTest.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/LazyElementCollectionBasicNonUniqueIdWhereTest.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/LazyElementCollectionWithLazyManyToOneNonUniqueIdWhereTest.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/LazyElementCollectionWithLazyManyToOneNonUniqueIdWhereTest.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/LazyElementCollectionWithLazyManyToOneNonUniqueIdWhereTest.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/LazyElementCollectionWithLazyManyToOneNonUniqueIdWhereTest.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/LazyManyToManyNonUniqueIdNotFoundWhereTest.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/LazyManyToManyNonUniqueIdNotFoundWhereTest.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/LazyManyToManyNonUniqueIdNotFoundWhereTest.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/LazyManyToManyNonUniqueIdNotFoundWhereTest.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/LazyManyToManyNonUniqueIdWhereTest.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/LazyManyToManyNonUniqueIdWhereTest.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/LazyManyToManyNonUniqueIdWhereTest.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/LazyManyToManyNonUniqueIdWhereTest.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/LazyOneToManyNonUniqueIdWhereTest.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/LazyOneToManyNonUniqueIdWhereTest.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/LazyOneToManyNonUniqueIdWhereTest.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/LazyOneToManyNonUniqueIdWhereTest.hbm.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/LazyToManyWhere.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/LazyToManyWhere.hbm.xml similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/where/hbm/LazyToManyWhere.hbm.xml rename to hibernate-core/src/test/resources/org/hibernate/orm/test/where/hbm/LazyToManyWhere.hbm.xml From 98ba4fca29bb2d347f843b45e778beb57ed57455 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Mon, 11 Dec 2023 13:49:39 +0100 Subject: [PATCH 015/321] HHH-17483 Add test for issue --- .../ManyToOneInheritanceSubTypeTest.java | 216 +++++++++++++----- 1 file changed, 159 insertions(+), 57 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/ManyToOneInheritanceSubTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/ManyToOneInheritanceSubTypeTest.java index 6eff309baec7..9511fe28a81d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/ManyToOneInheritanceSubTypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/ManyToOneInheritanceSubTypeTest.java @@ -14,55 +14,95 @@ import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import jakarta.persistence.DiscriminatorColumn; import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.Inheritance; import jakarta.persistence.InheritanceType; import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import static org.assertj.core.api.Assertions.assertThat; /** * @author Laurent Almeras + * @author Marco Belladelli */ @DomainModel( annotatedClasses = { - ManyToOneInheritanceSubTypeTest.SuperType.class, - ManyToOneInheritanceSubTypeTest.TypeA.class, - ManyToOneInheritanceSubTypeTest.TypeB.class, - ManyToOneInheritanceSubTypeTest.LinkedEntity.class + ManyToOneInheritanceSubTypeTest.LinkedEntity.class, + ManyToOneInheritanceSubTypeTest.SingleTableEntity.class, + ManyToOneInheritanceSubTypeTest.SingleA.class, + ManyToOneInheritanceSubTypeTest.SubSingleA.class, + ManyToOneInheritanceSubTypeTest.SingleB.class, + ManyToOneInheritanceSubTypeTest.JoinedEntity.class, + ManyToOneInheritanceSubTypeTest.JoinedA.class, + ManyToOneInheritanceSubTypeTest.SubJoinedA.class, + ManyToOneInheritanceSubTypeTest.JoinedB.class, + ManyToOneInheritanceSubTypeTest.UnionEntity.class, + ManyToOneInheritanceSubTypeTest.UnionA.class, + ManyToOneInheritanceSubTypeTest.SubUnionA.class, + ManyToOneInheritanceSubTypeTest.UnionB.class, } ) @SessionFactory( useCollectingStatementInspector = true ) @Jira( "https://hibernate.atlassian.net/browse/HHH-16616" ) +@Jira( "https://hibernate.atlassian.net/browse/HHH-17483" ) public class ManyToOneInheritanceSubTypeTest { @BeforeAll public void setUp(SessionFactoryScope scope) { scope.inTransaction( session -> { - final SuperType superType = new SuperType( 1 ); - final TypeB typeB = new TypeB( 2, "typeB" ); - final TypeA typeA = new TypeA( 3, "typeA" ); - final LinkedEntity entity = new LinkedEntity( 4 ); - entity.addTypeA( typeA ); - session.persist( superType ); - session.persist( typeB ); - session.persist( typeA ); + final LinkedEntity entity = new LinkedEntity( 1 ); + final SingleA singleA = new SingleA(); + session.persist( singleA ); + entity.getSingle().add( singleA ); + final SubSingleA subSingleA = new SubSingleA(); + session.persist( subSingleA ); + entity.getSingle().add( subSingleA ); + session.persist( new SingleB() ); + final JoinedA joinedA = new JoinedA(); + session.persist( joinedA ); + entity.getJoined().add( joinedA ); + final SubJoinedA subJoinedA = new SubJoinedA(); + session.persist( subJoinedA ); + entity.getJoined().add( subJoinedA ); + session.persist( new JoinedB() ); + final UnionA unionA = new UnionA(); + unionA.setLinkedEntity( entity ); + session.persist( unionA ); + final SubUnionA subUnionA = new SubUnionA(); + subUnionA.setLinkedEntity( entity ); + session.persist( subUnionA ); + session.persist( new UnionB() ); session.persist( entity ); } ); } + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.createMutationQuery( "delete from SingleTableEntity" ).executeUpdate(); + session.createMutationQuery( "delete from JoinedEntity" ).executeUpdate(); + session.createMutationQuery( "delete from UnionEntity" ).executeUpdate(); + session.createMutationQuery( "delete from LinkedEntity" ).executeUpdate(); + } ); + } + @Test public void testFind(SessionFactoryScope scope) { final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); inspector.clear(); scope.inTransaction( session -> { - final LinkedEntity entity = session.find( LinkedEntity.class, 4 ); - assertThat( entity.getTypeAS() ).hasSize( 1 ); - inspector.assertExecutedCount( 2 ); - inspector.assertNumberOfOccurrenceInQueryNoSpace( 1, "disc_col", 1 ); + final LinkedEntity entity = session.find( LinkedEntity.class, 1 ); + inspector.clear(); + assertThat( entity.getSingle() ).hasSize( 2 ); + inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "disc_col", 2 ); + assertThat( entity.getJoined() ).hasSize( 2 ); + assertThat( entity.getUnion() ).hasSize( 2 ); } ); } @@ -72,78 +112,140 @@ public void testJoinFetch(SessionFactoryScope scope) { inspector.clear(); scope.inTransaction( session -> { final LinkedEntity entity = session.createQuery( - "from LinkedEntity e left join fetch e.typeAS", + "from LinkedEntity e join fetch e.single", + LinkedEntity.class + ).getSingleResult(); + assertThat( entity.getSingle() ).hasSize( 2 ); + inspector.assertExecutedCount( 1 ); + inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "disc_col", 2 ); + } ); + inspector.clear(); + scope.inTransaction( session -> { + final LinkedEntity entity = session.createQuery( + "from LinkedEntity e join fetch e.joined", + LinkedEntity.class + ).getSingleResult(); + assertThat( entity.getJoined() ).hasSize( 2 ); + inspector.assertExecutedCount( 1 ); + } ); + inspector.clear(); + scope.inTransaction( session -> { + final LinkedEntity entity = session.createQuery( + "from LinkedEntity e join fetch e.union", LinkedEntity.class ).getSingleResult(); - assertThat( entity.getTypeAS() ).hasSize( 1 ); + assertThat( entity.getUnion() ).hasSize( 2 ); inspector.assertExecutedCount( 1 ); - inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "disc_col", 1 ); } ); } - @Entity( name = "SuperType" ) - @Inheritance( strategy = InheritanceType.SINGLE_TABLE ) - @DiscriminatorColumn( name = "disc_col" ) - public static class SuperType { + @Entity( name = "LinkedEntity" ) + public static class LinkedEntity { @Id private Integer id; - public SuperType() { + @OneToMany + @JoinColumn( name = "single_id" ) + private List single = new ArrayList<>(); + + @OneToMany + @JoinColumn( name = "joined_id" ) + private List joined = new ArrayList<>(); + + @OneToMany( mappedBy = "linkedEntity" ) + private List union = new ArrayList<>(); + + public LinkedEntity() { } - public SuperType(Integer id) { + public LinkedEntity(Integer id) { this.id = id; } - } - @Entity( name = "TypeA" ) - public static class TypeA extends SuperType { - private String typeAName; + public List getSingle() { + return single; + } - public TypeA() { + public List getJoined() { + return joined; } - public TypeA(Integer id, String typeAName) { - super( id ); - this.typeAName = typeAName; + public List getUnion() { + return union; } } - @Entity( name = "TypeB" ) - public static class TypeB extends SuperType { - private String typeBName; + // InheritanceType.SINGLE_TABLE - public TypeB() { - } + @Entity( name = "SingleTableEntity" ) + @Inheritance( strategy = InheritanceType.SINGLE_TABLE ) + @DiscriminatorColumn( name = "disc_col" ) + public static class SingleTableEntity { + @Id + @GeneratedValue + private Integer id; + } - public TypeB(Integer id, String typeBName) { - super( id ); - this.typeBName = typeBName; - } + @Entity( name = "SingleA" ) + public static class SingleA extends SingleTableEntity { } - @Entity( name = "LinkedEntity" ) - public static class LinkedEntity { + @Entity( name = "SubSingleA" ) + public static class SubSingleA extends SingleA { + } + + @Entity( name = "SingleB" ) + public static class SingleB extends SingleTableEntity { + } + + // InheritanceType.JOINED + + @Entity( name = "JoinedEntity" ) + @Inheritance( strategy = InheritanceType.JOINED ) + public static class JoinedEntity { @Id + @GeneratedValue private Integer id; + } - @OneToMany - @JoinColumn( name = "linked_id" ) - private List typeAS = new ArrayList<>(); + @Entity( name = "JoinedA" ) + public static class JoinedA extends JoinedEntity { + } - public LinkedEntity() { - } + @Entity( name = "SubJoinedA" ) + public static class SubJoinedA extends JoinedA { + } - public LinkedEntity(Integer id) { - this.id = id; - } + @Entity( name = "JoinedB" ) + public static class JoinedB extends JoinedEntity { + } - public List getTypeAS() { - return typeAS; - } + // InheritanceType.TABLE_PER_CLASS + + @Entity( name = "UnionEntity" ) + @Inheritance( strategy = InheritanceType.TABLE_PER_CLASS ) + public static class UnionEntity { + @Id + @GeneratedValue + private Integer id; + } + + @Entity( name = "UnionA" ) + public static class UnionA extends UnionEntity { + @ManyToOne + @JoinColumn( name = "linked_id" ) + private LinkedEntity linkedEntity; - public void addTypeA(TypeA typeA) { - this.typeAS.add( typeA ); + public void setLinkedEntity(LinkedEntity linkedEntity) { + this.linkedEntity = linkedEntity; } } + + @Entity( name = "SubUnionA" ) + public static class SubUnionA extends UnionA { + } + + @Entity( name = "UnionB" ) + public static class UnionB extends UnionEntity { + } } From 5a27b2a4da3470fd37e6d91a7ee0222e07d9c37d Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Mon, 11 Dec 2023 11:06:58 +0100 Subject: [PATCH 016/321] HHH-17483 Fix applyDiscriminator treat for nested inheritance subtypes Also small fix to joined-inheritance pruning. --- .../entity/AbstractEntityPersister.java | 22 +++++++++++-- .../entity/JoinedSubclassEntityPersister.java | 16 ++++++---- .../entity/SingleTableEntityPersister.java | 2 +- .../entity/UnionSubclassEntityPersister.java | 2 +- .../sqm/sql/BaseSqmToSqlAstConverter.java | 32 +++++++++++++------ 5 files changed, 53 insertions(+), 21 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 40ff18a7d4fe..598fad93bb7d 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -3037,7 +3037,7 @@ protected void logStaticSQL() { public abstract Map getSubclassByDiscriminatorValue(); - protected abstract boolean needsDiscriminator(); + public abstract boolean needsDiscriminator(); protected boolean isDiscriminatorFormula() { return false; @@ -3130,7 +3130,25 @@ public void applyDiscriminator( TableGroup tableGroup, SqlAstCreationState creationState) { if ( needsDiscriminator() ) { - pruneForSubclasses( tableGroup, Collections.singletonMap( getEntityName(), EntityNameUse.TREAT ) ); + assert !creationState.supportsEntityNameUsage() : "Entity name usage should have been used instead"; + final Map entityNameUseMap; + final Collection subMappingTypes = getSubMappingTypes(); + if ( subMappingTypes.isEmpty() ) { + entityNameUseMap = Collections.singletonMap( getEntityName(), EntityNameUse.TREAT ); + } + else { + entityNameUseMap = new HashMap<>( 1 + subMappingTypes.size() ); + entityNameUseMap.put( getEntityName(), EntityNameUse.TREAT ); + // We need to register TREAT uses for all subtypes when pruning + for ( EntityMappingType subMappingType : subMappingTypes ) { + entityNameUseMap.put( subMappingType.getEntityName(), EntityNameUse.TREAT ); + } + if ( isInherited() ) { + // Make sure the table group includes the root table when needed for TREAT + tableGroup.resolveTableReference( getRootTableName() ); + } + } + pruneForSubclasses( tableGroup, entityNameUseMap ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java index 689b40f38467..4d4e918f8c8e 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java @@ -744,7 +744,7 @@ private void associateSubclassNamesToSubclassTableIndex( } @Override - protected boolean needsDiscriminator() { + public boolean needsDiscriminator() { return forceDiscriminator; } @@ -1325,15 +1325,17 @@ public void pruneForSubclasses(TableGroup tableGroup, Map final String[] subclassTableNames = persister.getSubclassTableNames(); // Build the intersection of all tables names that are of the class or super class // These are the tables that can be safely inner joined - if ( tablesToInnerJoin.isEmpty() ) { - for ( int i = 0; i < subclassTableNames.length; i++ ) { - if ( persister.isClassOrSuperclassTable[i] ) { - tablesToInnerJoin.add( subclassTableNames[i] ); - } + final Set classOrSuperclassTables = new HashSet<>( subclassTableNames.length ); + for ( int i = 0; i < subclassTableNames.length; i++ ) { + if ( persister.isClassOrSuperclassTable[i] ) { + classOrSuperclassTables.add( subclassTableNames[i] ); } } + if ( tablesToInnerJoin.isEmpty() ) { + tablesToInnerJoin.addAll( classOrSuperclassTables ); + } else { - tablesToInnerJoin.retainAll( Arrays.asList( subclassTableNames ) ); + tablesToInnerJoin.retainAll( classOrSuperclassTables ); } if ( useKind == EntityNameUse.UseKind.FILTER && explicitDiscriminatorColumnName == null ) { // If there is no discriminator column, diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java index c046aa64a0f1..6cff99cb9927 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java @@ -548,7 +548,7 @@ public String fromTableFragment(String name) { } @Override - protected boolean needsDiscriminator() { + public boolean needsDiscriminator() { return forceDiscriminator || isInherited(); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java index d98449cdf4c7..9880922620b3 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java @@ -286,7 +286,7 @@ public TableGroup createRootTableGroup( } @Override - protected boolean needsDiscriminator() { + public boolean needsDiscriminator() { return false; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 6bf4fdedba07..b6ba343088f0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -3102,20 +3102,30 @@ private void registerEntityNameUsage( ); } } - if ( useKind == EntityNameUse.UseKind.TREAT || useKind == EntityNameUse.UseKind.PROJECTION ) { - // If we encounter a treat use, we also want register the use for all subtypes. - // We do this here to not have to expand entity name uses during pruning later on + // If we encounter a treat or projection use, we also want register the use for all subtypes. + // We do this here to not have to expand entity name uses during pruning later on + if ( useKind == EntityNameUse.UseKind.TREAT ) { for ( EntityMappingType subType : persister.getSubMappingTypes() ) { entityNameUses.compute( subType.getEntityName(), (s, existingUse) -> finalEntityNameUse.stronger( existingUse ) ); - if ( useKind == EntityNameUse.UseKind.PROJECTION ) { - actualTableGroup.resolveTableReference( - null, - subType.getEntityPersister().getMappedTableDetails().getTableName() - ); - } + } + if ( persister.isInherited() && persister.needsDiscriminator() ) { + // Make sure the table group includes the root table when needed for TREAT + actualTableGroup.resolveTableReference( persister.getRootTableName() ); + } + } + else if ( useKind == EntityNameUse.UseKind.PROJECTION ) { + for ( EntityMappingType subType : persister.getSubMappingTypes() ) { + entityNameUses.compute( + subType.getEntityName(), + (s, existingUse) -> finalEntityNameUse.stronger( existingUse ) + ); + actualTableGroup.resolveTableReference( + null, + subType.getEntityPersister().getMappedTableDetails().getTableName() + ); } } } @@ -8217,7 +8227,9 @@ else if ( getLoadQueryInfluencers().hasEnabledFetchProfiles() ) { if ( entityMappingType.getSuperMappingType() != null ) { // A joined table group was created by an enabled entity graph or fetch profile, // and it's of an inheritance subtype, so we should apply the discriminator - entityMappingType.applyDiscriminator( null, null, actualTableGroup, this ); + getCurrentClauseStack().push( Clause.FROM ); + registerEntityNameUsage( actualTableGroup, EntityNameUse.TREAT, entityMappingType.getEntityName() ); + getCurrentClauseStack().pop(); } } } From 6e79643a550134cf82a1a95e7b48c00ab0491a6c Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Mon, 8 Jan 2024 17:22:23 +0100 Subject: [PATCH 017/321] HHH-17623 Test and fix use of association in @OrderBy --- .../mapping/internal/AbstractDomainPath.java | 16 +- .../ast/FkDomainPathContinuation.java | 33 ++- .../orm/test/orderby/OrderByToOneTest.java | 204 ++++++++++++++++++ 3 files changed, 221 insertions(+), 32 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/orderby/OrderByToOneTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDomainPath.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDomainPath.java index 0aa6733960f0..a4fe12bc8fd1 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDomainPath.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDomainPath.java @@ -12,6 +12,7 @@ import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EntityValuedModelPart; +import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.ordering.ast.DomainPath; @@ -161,19 +162,8 @@ else if ( referenceModelPart instanceof EntityValuedModelPart ) { subPart = ( (EntityValuedModelPart) referenceModelPart ).getEntityMappingType().getIdentifierMapping(); } else { - subPart = ( (EntityValuedModelPart) referenceModelPart ).findSubPart( modelPartName ); - if ( subPart == null && referenceModelPart instanceof ToOneAttributeMapping ) { - // this is the case of sort by to-one attribute inside an embedded item, - // at this stage the foreign key descriptor should have been set on the attribute mapping, - // so we can generate a sub part valid for the order-by generation - ToOneAttributeMapping toOneAttribute = (ToOneAttributeMapping) referenceModelPart; - String foreignKeyModelPart = toOneAttribute.getAttributeName() + "." - + toOneAttribute.getTargetKeyPropertyName(); - - if ( modelPartName.equals( foreignKeyModelPart ) ) { - subPart = toOneAttribute.findSubPart( toOneAttribute.getTargetKeyPropertyName() ); - } - } + // Default to using the foreign key of an entity valued model part + subPart = ( (EntityValuedModelPart) referenceModelPart ).findSubPart( ForeignKeyDescriptor.PART_NAME ); } apply( subPart, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ordering/ast/FkDomainPathContinuation.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ordering/ast/FkDomainPathContinuation.java index 8fedb5591f71..571a69bbf311 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ordering/ast/FkDomainPathContinuation.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ordering/ast/FkDomainPathContinuation.java @@ -21,15 +21,18 @@ public class FkDomainPathContinuation extends DomainPathContinuation { private final Set possiblePaths; public FkDomainPathContinuation( - NavigablePath navigablePath, DomainPath lhs, + NavigablePath navigablePath, + DomainPath lhs, ToOneAttributeMapping referencedModelPart) { super( navigablePath, lhs, referencedModelPart ); this.possiblePaths = referencedModelPart.getTargetKeyPropertyNames(); } public FkDomainPathContinuation( - NavigablePath navigablePath, DomainPath lhs, - ModelPart referencedModelPart, Set possiblePaths) { + NavigablePath navigablePath, + DomainPath lhs, + ModelPart referencedModelPart, + Set possiblePaths) { super( navigablePath, lhs, referencedModelPart ); this.possiblePaths = possiblePaths; } @@ -40,25 +43,17 @@ public SequencePart resolvePathPart( String identifier, boolean isTerminal, TranslationContext translationContext) { - HashSet furtherPaths = new LinkedHashSet<>( possiblePaths.size() ); - for ( String path : possiblePaths ) { - if ( !path.startsWith( name ) ) { - return new DomainPathContinuation( - navigablePath.append( name ), - this, - // unfortunately at this stage the foreign key descriptor could not be set - // on the attribute mapping yet, so we need to defer the sub part extraction later - referencedModelPart - ); - } - - furtherPaths.add( path.substring( name.length() + 1 ) ); - } - - if ( furtherPaths.isEmpty() ) { + if ( !possiblePaths.contains( name ) ) { throw new PathResolutionException( "Domain path of type `" + referencedModelPart.getPartMappingType() + "` -> `" + name + "`" ); } + final HashSet furtherPaths = new HashSet<>(); + for ( String possiblePath : possiblePaths ) { + if ( possiblePath.startsWith( name ) && possiblePath.length() > name.length() + && possiblePath.charAt( name.length() ) == '.' ) { + furtherPaths.add( possiblePath.substring( name.length() + 2 ) ); + } + } return new FkDomainPathContinuation( navigablePath.append( name ), this, diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/orderby/OrderByToOneTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/orderby/OrderByToOneTest.java new file mode 100644 index 000000000000..38eac1f57982 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/orderby/OrderByToOneTest.java @@ -0,0 +1,204 @@ +package org.hibernate.orm.test.orderby; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Christian Beikov + */ +@DomainModel( + annotatedClasses = { + OrderByToOneTest.Task.class, + OrderByToOneTest.TaskVersion.class, + OrderByToOneTest.User.class + } +) +@SessionFactory +public class OrderByToOneTest { + + @BeforeEach + protected void prepareTest(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + User u1 = new User( 1L, "u1" ); + session.persist( u1 ); + User u2 = new User( 2L, "u2" ); + session.persist( u2 ); + + Task t = new Task(); + t.setId( 1L ); + TaskVersion tv1 = new TaskVersion(); + tv1.setName( "tv1" ); + tv1.setAssignee( u2 ); + List versions = new ArrayList<>(); + versions.add( tv1 ); + t.setTaskVersions( versions ); + tv1.setTask( t ); + + TaskVersion tv2 = new TaskVersion(); + tv2.setName( "tv2" ); + tv2.setAssignee( u1 ); + t.getTaskVersions().add( tv2 ); + tv2.setTask( t ); + session.persist( t ); + } + ); + } + + @AfterEach + protected void cleanupTest(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createMutationQuery( "delete from TaskVersion" ).executeUpdate(); + session.createMutationQuery( "delete from UUser" ).executeUpdate(); + session.createMutationQuery( "delete from Task" ).executeUpdate(); + } + ); + } + + @Test + @JiraKey("HHH-17623") + public void testOrderByToOne(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + final Task task = session.createQuery( "from Task t", Task.class ).getSingleResult(); + final List taskVersions = task.getTaskVersions(); + assertEquals( 2, taskVersions.size() ); + assertEquals( "tv2", taskVersions.get( 0 ).getName() ); + assertEquals( "tv1", taskVersions.get( 1 ).getName() ); + } + ); + } + + @Entity(name = "Task") + public static class Task { + + @Id + private Long id; + + @OneToMany(mappedBy = "task", cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @OrderBy("assignee ASC") + private List taskVersions; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public List getTaskVersions() { + return taskVersions; + } + + public void setTaskVersions(List taskVersions) { + this.taskVersions = taskVersions; + } + } + + @Entity(name = "TaskVersion") + public static class TaskVersion { + + @Id + @GeneratedValue + private Long id; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "assignee", nullable = true) + private User assignee; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "task_id", nullable = false) + private Task task; + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Task getTask() { + return task; + } + + public void setTask(Task task) { + this.task = task; + } + + public User getAssignee() { + return assignee; + } + + public void setAssignee(User assignee) { + this.assignee = assignee; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + } + + @Entity(name = "UUser") + 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 void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + +} From 24b516253adadff9348827ebd5538fc2f4ffcf71 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 8 Jan 2024 17:19:02 +0100 Subject: [PATCH 018/321] HHH-17307 Hibernate 6 ListResultsConsumer.Results#addUnique really slow for ElementCollections --- .../loader/ast/internal/CollectionLoaderSubSelectFetch.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSubSelectFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSubSelectFetch.java index 67118de2006d..967e94d9b498 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSubSelectFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSubSelectFetch.java @@ -138,7 +138,7 @@ public PersistentCollection load(Object triggerKey, SharedSessionContractImpl this.subselect.getLoadingJdbcParameterBindings(), new ExecutionContextWithSubselectFetchHandler( session, subSelectFetchableKeysHandler ), RowTransformerStandardImpl.instance(), - ListResultsConsumer.UniqueSemantic.FILTER + ListResultsConsumer.UniqueSemantic.NONE ); if ( subSelectFetchedCollections != null && ! subSelectFetchedCollections.isEmpty() ) { From 8f2321ef6b199b201b9a00afbffc08c294fcaf5e Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Mon, 8 Jan 2024 18:17:00 +0100 Subject: [PATCH 019/321] HHH-17616 - Move resources that were still in src/java to src/resources (hibernate-community-dialects) Signed-off-by: Jan Schatteman --- .../org/hibernate/community/dialect/Person.hbm.xml | 0 .../functional/cache/TestInterSystemsFunctionsClass.hbm.xml | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename hibernate-community-dialects/src/test/{java => resources}/org/hibernate/community/dialect/Person.hbm.xml (100%) rename hibernate-community-dialects/src/test/{java => resources}/org/hibernate/community/dialect/functional/cache/TestInterSystemsFunctionsClass.hbm.xml (100%) diff --git a/hibernate-community-dialects/src/test/java/org/hibernate/community/dialect/Person.hbm.xml b/hibernate-community-dialects/src/test/resources/org/hibernate/community/dialect/Person.hbm.xml similarity index 100% rename from hibernate-community-dialects/src/test/java/org/hibernate/community/dialect/Person.hbm.xml rename to hibernate-community-dialects/src/test/resources/org/hibernate/community/dialect/Person.hbm.xml diff --git a/hibernate-community-dialects/src/test/java/org/hibernate/community/dialect/functional/cache/TestInterSystemsFunctionsClass.hbm.xml b/hibernate-community-dialects/src/test/resources/org/hibernate/community/dialect/functional/cache/TestInterSystemsFunctionsClass.hbm.xml similarity index 100% rename from hibernate-community-dialects/src/test/java/org/hibernate/community/dialect/functional/cache/TestInterSystemsFunctionsClass.hbm.xml rename to hibernate-community-dialects/src/test/resources/org/hibernate/community/dialect/functional/cache/TestInterSystemsFunctionsClass.hbm.xml From e32d11ef6bedc93df40d5ce45c5ce5129ee2ed05 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Tue, 7 Nov 2023 10:23:35 +0100 Subject: [PATCH 020/321] HHH-17395 test case --- .../locking/LockRefreshReferencedTest.java | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockRefreshReferencedTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockRefreshReferencedTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockRefreshReferencedTest.java new file mode 100644 index 000000000000..25f62e1dcc80 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockRefreshReferencedTest.java @@ -0,0 +1,180 @@ +package org.hibernate.orm.test.locking; + +import java.util.List; + +import org.hibernate.Hibernate; + +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.LockModeType; +import jakarta.persistence.OneToOne; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@JiraKey("HHH-17395") +@Jpa( + annotatedClasses = { + LockRefreshReferencedTest.MainEntity.class, + LockRefreshReferencedTest.ReferencedEntity.class + } +) +public class LockRefreshReferencedTest { + + @BeforeAll + public void setUp(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + final ReferencedEntity e1 = new ReferencedEntity( 0L, "lazy" ); + final ReferencedEntity e2 = new ReferencedEntity( 1L, "eager" ); + entityManager.persist( e1 ); + entityManager.persist( e2 ); + final MainEntity e3 = new MainEntity( 0L, e1, e2 ); + entityManager.persist( e3 ); + } + ); + } + + @Test + public void testRefreshBeforeRead(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + MainEntity m = entityManager.find( MainEntity.class, 0L ); + assertNotNull( m ); + ReferencedEntity lazyReference = m.referencedLazy(); + ReferencedEntity eagerReference = m.referencedEager(); + assertNotNull( lazyReference ); + assertNotNull( eagerReference ); + assertFalse( Hibernate.isInitialized( lazyReference ) ); + + // First refresh, then access + entityManager.refresh( eagerReference, LockModeType.PESSIMISTIC_WRITE ); + assertFalse( Hibernate.isInitialized( lazyReference ) ); + + entityManager.refresh( lazyReference, LockModeType.PESSIMISTIC_WRITE ); + + assertEquals( "lazy", lazyReference.status() ); + assertEquals( "eager", eagerReference.status() ); + assertEquals( LockModeType.PESSIMISTIC_WRITE, entityManager.getLockMode( lazyReference ) ); + assertEquals( LockModeType.PESSIMISTIC_WRITE, entityManager.getLockMode( eagerReference ) ); + } ); + } + + @Test + public void testRefresh(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + MainEntity m = entityManager.find( MainEntity.class, 0L ); + assertNotNull( m ); + ReferencedEntity lazyReference = m.referencedLazy(); + ReferencedEntity eagerReference = m.referencedEager(); + assertNotNull( lazyReference ); + assertNotNull( eagerReference ); + assertFalse( Hibernate.isInitialized( lazyReference ) ); + + entityManager.refresh( m ); + assertFalse( Hibernate.isInitialized( lazyReference ) ); + + } ); + } + + @Test + public void testRefreshAfterRead(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + MainEntity m = entityManager.find( MainEntity.class, 0L ); + assertNotNull( m ); + ReferencedEntity lazyReference = m.referencedLazy(); + ReferencedEntity eagerReference = m.referencedEager(); + assertNotNull( lazyReference ); + assertNotNull( eagerReference ); + assertFalse( Hibernate.isInitialized( lazyReference ) ); + + // First access, the refresh + assertEquals( "lazy", lazyReference.status() ); + assertEquals( "eager", eagerReference.status() ); + + entityManager.refresh( lazyReference, LockModeType.PESSIMISTIC_WRITE ); + entityManager.refresh( eagerReference, LockModeType.PESSIMISTIC_WRITE ); + + assertEquals( LockModeType.PESSIMISTIC_WRITE, entityManager.getLockMode( lazyReference ) ); + assertEquals( LockModeType.PESSIMISTIC_WRITE, entityManager.getLockMode( eagerReference ) ); + } ); + } + + + @Test + public void testFindWithLockMode(EntityManagerFactoryScope scope) { + scope.inTransaction( + session -> { + MainEntity mainEntity = session.find( MainEntity.class, 0L, LockModeType.PESSIMISTIC_WRITE ); + assertThat( session.getLockMode( mainEntity.referencedEager() ) ).isEqualTo( LockModeType.PESSIMISTIC_WRITE ); + } + ); + } + + @Entity(name = "MainEntity") + public static class MainEntity { + @Id + private Long id; + + private String name; + + @OneToOne(targetEntity = ReferencedEntity.class, fetch = FetchType.LAZY) + @JoinColumn(name = "LAZY_COLUMN") + private ReferencedEntity referencedLazy; + + @OneToOne(targetEntity = ReferencedEntity.class, fetch = FetchType.EAGER) + @JoinColumn(name = "EAGER_COLUMN") + private ReferencedEntity referencedEager; + + protected MainEntity() { + } + + public MainEntity(Long id, ReferencedEntity lazy, ReferencedEntity eager) { + this.id = id; + this.referencedLazy = lazy; + this.referencedEager = eager; + } + + public ReferencedEntity referencedLazy() { + return referencedLazy; + } + + public ReferencedEntity referencedEager() { + return referencedEager; + } + } + + @Entity(name = "ReferencedEntity") + public static class ReferencedEntity { + + @Id + private Long id; + + private String status; + + protected ReferencedEntity() { + } + + public ReferencedEntity(Long id, String status) { + this.id = id; + this.status = status; + } + + public String status() { + return status; + } + } + +} From 49190fda660326d4b72ef6adf3995afc37ea30b3 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 8 Nov 2023 15:29:42 +0100 Subject: [PATCH 021/321] HHH-1645 enabled test for issue --- .../test/java/org/hibernate/orm/test/proxy/ProxyTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyTest.java index 0fd01871918c..caca8b052914 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyTest.java @@ -24,8 +24,8 @@ import org.hibernate.internal.util.SerializationHelper; import org.hibernate.proxy.HibernateProxy; -import org.hibernate.testing.FailureExpected; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.orm.junit.JiraKey; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -539,7 +539,7 @@ public void testRefreshLockInitializedProxy() { } @Test - @FailureExpected( jiraKey = "HHH-1645", message = "Session.refresh with LockOptions does not work on uninitialized proxies" ) + @JiraKey( "HHH-1645" ) public void testRefreshLockUninitializedProxy() { Session s = openSession(); Transaction t = s.beginTransaction(); @@ -568,7 +568,7 @@ private static DataPoint newPersistentDataPoint(Session s) { } @Test - @FailureExpected( jiraKey = "HHH-1645", message = "Session.refresh with LockOptions does not work on uninitialized proxies" ) + @JiraKey( "HHH-1645" ) public void testRefreshLockUninitializedProxyThenRead() { Session s = openSession(); Transaction t = s.beginTransaction(); From 2555a05cb6e489c986d28b18ae06a6fb1f061aff Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 18 Dec 2023 13:07:00 +0100 Subject: [PATCH 022/321] HHH-1645 HHH-17395 Refresh with LockMode on an unitialized proxy does not work --- .../internal/DefaultRefreshEventListener.java | 66 +++++++++++++++---- .../orm/test/readonly/ReadOnlyProxyTest.java | 4 +- .../test/readonly/ReadOnlySessionTest.java | 8 +-- 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java index ac260791fb12..f9ef7d2fbf33 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java @@ -32,6 +32,8 @@ import org.hibernate.metamodel.spi.MappingMetamodelImplementor; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.proxy.HibernateProxy; +import org.hibernate.proxy.LazyInitializer; import org.hibernate.type.CollectionType; import org.hibernate.type.CompositeType; import org.hibernate.type.Type; @@ -61,7 +63,22 @@ public void onRefresh(RefreshEvent event, RefreshContext refreshedAlready) { final PersistenceContext persistenceContext = source.getPersistenceContextInternal(); final Object object = event.getObject(); if ( persistenceContext.reassociateIfUninitializedProxy( object ) ) { - if ( isTransient( event, source, object ) ) { + final boolean isTransient = isTransient( event, source, object ); + + final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( object ); + final EntityPersister persister = source.getEntityPersister( lazyInitializer.getEntityName(), object ); + refresh( + event, + null, + source, + persister, + lazyInitializer, + null, + persister.getIdentifier( object, event.getSession() ), + persistenceContext + ); + + if ( isTransient ) { source.setReadOnly( object, source.isDefaultReadOnly() ); } } @@ -147,9 +164,22 @@ private static void refresh(RefreshEvent event, RefreshContext refreshedAlready, evictEntity( object, persister, id, source ); evictCachedCollections( persister, id, source ); + refresh( event, object, source, persister, null, entry, id, persistenceContext ); + } + + private static void refresh( + RefreshEvent event, + Object object, + EventSource source, + EntityPersister persister, + LazyInitializer lazyInitializer, + EntityEntry entry, + Object id, + PersistenceContext persistenceContext) { + final Object result = source.getLoadQueryInfluencers().fromInternalFetchProfile( CascadingFetchProfile.REFRESH, - () -> doRefresh( event, source, object, entry, persister, id, persistenceContext ) + () -> doRefresh( event, source, object, entry, persister, lazyInitializer, id, persistenceContext ) ); UnresolvableObjectException.throwIfNull( result, id, persister.getEntityName() ); } @@ -182,6 +212,7 @@ private static Object doRefresh( Object object, EntityEntry entry, EntityPersister persister, + LazyInitializer lazyInitializer, Object id, PersistenceContext persistenceContext) { // Handle the requested lock-mode (if one) in relation to the entry's (if one) current lock-mode @@ -231,19 +262,32 @@ private static Object doRefresh( persistenceContext.getEntry( result ).setLockMode( postRefreshLockMode ); } - // Keep the same read-only/modifiable setting for the entity that it had before refreshing; - // If it was transient, then set it to the default for the source. - if ( !persister.isMutable() ) { - // this is probably redundant; it should already be read-only - source.setReadOnly( result, true ); - } - else { - source.setReadOnly( result, entry == null ? source.isDefaultReadOnly() : entry.isReadOnly() ); - } + source.setReadOnly( result, isReadOnly( entry, persister, lazyInitializer, source ) ); } return result; } + private static boolean isReadOnly( + EntityEntry entry, + EntityPersister persister, + LazyInitializer lazyInitializer, + EventSource source) { + // Keep the same read-only/modifiable setting for the entity that it had before refreshing; + // If it was transient, then set it to the default for the source. + if ( !persister.isMutable() ) { + return true; + } + else if ( entry != null ) { + return entry.isReadOnly(); + } + else if ( lazyInitializer != null ) { + return lazyInitializer.isReadOnly(); + } + else { + return source.isDefaultReadOnly(); + } + } + private static void evictCachedCollections(EntityPersister persister, Object id, EventSource source) { evictCachedCollections( persister.getPropertyTypes(), id, source ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/readonly/ReadOnlyProxyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/readonly/ReadOnlyProxyTest.java index 3832c9ee0340..fdd3588a6b13 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/readonly/ReadOnlyProxyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/readonly/ReadOnlyProxyTest.java @@ -1102,7 +1102,7 @@ public void testReadOnlyRefresh(SessionFactoryScope scope) { s.setReadOnly( dp, true ); assertFalse( Hibernate.isInitialized( dp ) ); s.refresh( dp ); - assertFalse( Hibernate.isInitialized( dp ) ); + assertTrue( Hibernate.isInitialized( dp ) ); assertEquals( "original", dp.getDescription() ); assertTrue( Hibernate.isInitialized( dp ) ); dp.setDescription( "changed" ); @@ -1232,7 +1232,7 @@ public void testReadOnlyRefreshDetached(SessionFactoryScope scope) { assertTrue( s.isReadOnly( dp ) ); s.evict( dp ); s.refresh( dp ); - assertFalse( Hibernate.isInitialized( dp ) ); + assertTrue( Hibernate.isInitialized( dp ) ); assertFalse( s.isReadOnly( dp ) ); dp.setDescription( "changed" ); assertEquals( "changed", dp.getDescription() ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/readonly/ReadOnlySessionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/readonly/ReadOnlySessionTest.java index 59897eba3f58..e46e1d5381bd 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/readonly/ReadOnlySessionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/readonly/ReadOnlySessionTest.java @@ -516,11 +516,11 @@ public void testReadOnlyProxyRefresh(SessionFactoryScope scope) { assertTrue( s.isReadOnly( dp ) ); assertFalse( Hibernate.isInitialized( dp ) ); s.refresh( dp ); - assertFalse( Hibernate.isInitialized( dp ) ); + assertTrue( Hibernate.isInitialized( dp ) ); assertTrue( s.isReadOnly( dp ) ); s.setDefaultReadOnly( false ); s.refresh( dp ); - assertFalse( Hibernate.isInitialized( dp ) ); + assertTrue( Hibernate.isInitialized( dp ) ); assertTrue( s.isReadOnly( dp ) ); assertEquals( "original", dp.getDescription() ); assertTrue( Hibernate.isInitialized( dp ) ); @@ -574,12 +574,12 @@ public void testReadOnlyProxyRefreshDetached(SessionFactoryScope scope) { assertTrue( s.isReadOnly( dp ) ); s.evict( dp ); s.refresh( dp ); - assertFalse( Hibernate.isInitialized( dp ) ); + assertTrue( Hibernate.isInitialized( dp ) ); s.setDefaultReadOnly( false ); assertTrue( s.isReadOnly( dp ) ); s.evict( dp ); s.refresh( dp ); - assertFalse( Hibernate.isInitialized( dp ) ); + assertTrue( Hibernate.isInitialized( dp ) ); assertFalse( s.isReadOnly( dp ) ); assertFalse( s.isReadOnly( ( (HibernateProxy) dp ).getHibernateLazyInitializer().getImplementation() ) ); dp.setDescription( "changed" ); From 872d9ef1f7c0c94c4db55076daabdd486bc0a9d5 Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Mon, 8 Jan 2024 23:59:17 +0100 Subject: [PATCH 023/321] HHH-17616 - More work related to moving resources Signed-off-by: Jan Schatteman --- .../main/asciidoc/userguide/chapters/events/Events.adoc | 3 ++- .../main/asciidoc/userguide/chapters/schema/Schema.adoc | 2 +- gradle/java-module.gradle | 7 ------- .../org/hibernate/orm/test/propertyref/import.sql | 0 .../org/hibernate/orm/test/schemamanager/data.sql | 0 hibernate-micrometer/hibernate-micrometer.gradle | 2 +- hibernate-spatial/hibernate-spatial.gradle | 2 +- 7 files changed, 5 insertions(+), 11 deletions(-) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/propertyref/import.sql (100%) rename hibernate-core/src/test/{java => resources}/org/hibernate/orm/test/schemamanager/data.sql (100%) diff --git a/documentation/src/main/asciidoc/userguide/chapters/events/Events.adoc b/documentation/src/main/asciidoc/userguide/chapters/events/Events.adoc index e8b9c8e638c8..d5eb2fc63043 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/events/Events.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/events/Events.adoc @@ -3,6 +3,7 @@ :root-project-dir: ../../../../../../.. :core-project-dir: {root-project-dir}/hibernate-core :example-dir-event: {core-project-dir}/src/test/java/org/hibernate/orm/test/events +:example-dir-event-resources: {core-project-dir}/src/test/resources/org/hibernate/orm/test/events :extrasdir: extras It is useful for the application to react to certain events that occur inside Hibernate. @@ -179,7 +180,7 @@ include::{example-dir-event}/DefaultEntityListener.java[tags=events-default-list [source, XML, indent=0] ---- -include::{example-dir-event}/DefaultEntityListener-orm.xml[tags=events-default-listener-mapping-example] +include::{example-dir-event-resources}/DefaultEntityListener-orm.xml[tags=events-default-listener-mapping-example] ---- ==== diff --git a/documentation/src/main/asciidoc/userguide/chapters/schema/Schema.adoc b/documentation/src/main/asciidoc/userguide/chapters/schema/Schema.adoc index 5749faddbf3c..61e030b59325 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/schema/Schema.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/schema/Schema.adoc @@ -82,7 +82,7 @@ Considering the following HBM mapping: ==== [source, JAVA, indent=0] ---- -include::{example-dir-schemagen}/SchemaGenerationTest.hbm.xml[] +include::{example-dir-schemagen-resources}/org/hibernate/orm/test/schema/SchemaGenerationTest.hbm.xml[] ---- ==== diff --git a/gradle/java-module.gradle b/gradle/java-module.gradle index c58fd59e132a..d7f9e5454899 100644 --- a/gradle/java-module.gradle +++ b/gradle/java-module.gradle @@ -245,13 +245,6 @@ tasks.withType( Test.class ).each { test -> sourceSets { test { resources { - // add `src/test/java` as a test-resources dir - configure( srcDir('src/test/java') ) { - filter { - include '**/*.properties' - include '**/*.xml' - } - } configure( srcDir('src/test/resources') ) { filter { include '*.properties' diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/import.sql b/hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/import.sql similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/propertyref/import.sql rename to hibernate-core/src/test/resources/org/hibernate/orm/test/propertyref/import.sql diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemamanager/data.sql b/hibernate-core/src/test/resources/org/hibernate/orm/test/schemamanager/data.sql similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/orm/test/schemamanager/data.sql rename to hibernate-core/src/test/resources/org/hibernate/orm/test/schemamanager/data.sql diff --git a/hibernate-micrometer/hibernate-micrometer.gradle b/hibernate-micrometer/hibernate-micrometer.gradle index 57e54a41cbe6..2f93bfd226e9 100644 --- a/hibernate-micrometer/hibernate-micrometer.gradle +++ b/hibernate-micrometer/hibernate-micrometer.gradle @@ -16,7 +16,7 @@ sourceSets { // resources inherently exclude sources test { resources { - setSrcDirs( ['src/test/java','src/test/resources'] ) + setSrcDirs( ['src/test/resources'] ) } } } diff --git a/hibernate-spatial/hibernate-spatial.gradle b/hibernate-spatial/hibernate-spatial.gradle index 34ba99f1647a..f17821309323 100644 --- a/hibernate-spatial/hibernate-spatial.gradle +++ b/hibernate-spatial/hibernate-spatial.gradle @@ -35,7 +35,7 @@ dependencies { } sourceSets.test.resources { - setSrcDirs( ['src/test/java', 'src/test/resources'] ) + setSrcDirs( ['src/test/resources'] ) } tasks.test { From 2ec2cf23432fbe49af7acc1ca1036129533bb1f7 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 9 Jan 2024 16:00:42 +0100 Subject: [PATCH 024/321] HHH-17621 Add test for issue --- .../test/any/annotations/AnyMergeTest.java | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/any/annotations/AnyMergeTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/any/annotations/AnyMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/any/annotations/AnyMergeTest.java new file mode 100644 index 000000000000..664bb1d54434 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/any/annotations/AnyMergeTest.java @@ -0,0 +1,145 @@ +package org.hibernate.orm.test.any.annotations; + +import java.math.BigDecimal; + +import org.hibernate.annotations.Any; +import org.hibernate.annotations.AnyDiscriminator; +import org.hibernate.annotations.AnyDiscriminatorValue; +import org.hibernate.annotations.AnyKeyJavaClass; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorType; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@DomainModel( + annotatedClasses = { + AnyMergeTest.InvoicePosition.class, + AnyMergeTest.Bonus.class, + AnyMergeTest.Fee.class, + AnyMergeTest.Amount.class + } +) +@SessionFactory +@JiraKey("HHH-17621") +public class AnyMergeTest { + + + @Test + public void testMerge(SessionFactoryScope scope) { + Amount amount = new Amount( new BigDecimal( 10 ) ); + Bonus bonus = new Bonus( "that's a bonus", amount ); + scope.inTransaction( + session -> { + session.persist( amount ); + session.persist( bonus ); + } + ); + + scope.inTransaction( + session -> { + InvoicePosition invoicePosition = new InvoicePosition(); + invoicePosition.setReference( bonus ); + InvoicePosition merged = session.merge( invoicePosition ); + + Reference mergedReference = merged.getReference(); + assertThat( mergedReference ).isExactlyInstanceOf( Bonus.class ); + Bonus mergedBonus = (Bonus) mergedReference; + // check the merged values are copies of the original ones + assertThat( mergedBonus ).isNotEqualTo( bonus ); + assertThat( mergedBonus.amount ).isNotEqualTo( amount ); + + assertThat( mergedBonus.amount.quantity.compareTo( new BigDecimal( 10 ) ) ).isEqualTo( 0 ); + } + ); + } + + public interface Reference { + } + + @Entity(name = "Bonus") + public static class Bonus implements Reference { + + @Id + @GeneratedValue + private Long id; + + private String name; + + @OneToOne + private Amount amount; + + public Bonus() { + } + + public Bonus(String name, Amount amount) { + this.name = name; + this.amount = amount; + } + } + + @Entity(name = "Amount") + public static class Amount { + + @Id + @GeneratedValue + private Long id; + + private BigDecimal quantity; + + public Amount() { + } + + public Amount(BigDecimal quantity) { + this.quantity = quantity; + } + } + + @Entity(name = "Fee") + public static class Fee implements Reference { + + @Id + @GeneratedValue + private Long id; + + private String name; + } + + @Entity(name = "InvoicePosition") + public static class InvoicePosition { + + @Id + @GeneratedValue + private Long id; + + @Any + @AnyDiscriminator(DiscriminatorType.STRING) + @AnyDiscriminatorValue(discriminator = "BONUS", entity = Bonus.class) + @AnyDiscriminatorValue(discriminator = "FEE", entity = Fee.class) + @AnyKeyJavaClass(Long.class) + @Column(name = "Type") + @JoinColumn(name = "ReferenceId") + private Reference reference; + + public Reference getReference() { + return reference; + } + + public void setReference(Reference reference) { + this.reference = reference; + } + } + + +} From 19081ba56560f586e1ac6c1d3d948ff9e110b36c Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 9 Jan 2024 16:14:48 +0100 Subject: [PATCH 025/321] HHH-17621 UnsupportedOperationException when merging an entity with a @Any mapping --- .../java/org/hibernate/type/TypeHelper.java | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java b/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java index 88652f4b1db5..d8407c72925b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java @@ -191,27 +191,26 @@ public static Object[] replaceAssociations( } else { final Type type = types[i]; - if ( type.isComponentType() ) { - final CompositeType compositeType = (CompositeType) type; - if ( target[i] != null ) { - // need to extract the component values and check for subtype replacements... - final Object[] objects = replaceCompositeAssociations( - session, - copyCache, - foreignKeyDirection, - target[i], - currentOriginal, - compositeType - ); - target[i] = compositeType.replacePropertyValues( target[i], objects, session ); - } - copied[i] = target[i]; - } - else if ( !type.isAssociationType() ) { - copied[i] = target[i]; + if ( type.isAssociationType() ) { + copied[i] = types[i].replace( currentOriginal, target[i], session, owner, copyCache, foreignKeyDirection ); } else { - copied[i] = types[i].replace( currentOriginal, target[i], session, owner, copyCache, foreignKeyDirection ); + if ( type.isComponentType() ) { + final CompositeType compositeType = (CompositeType) type; + if ( target[i] != null ) { + // need to extract the component values and check for subtype replacements... + final Object[] objects = replaceCompositeAssociations( + session, + copyCache, + foreignKeyDirection, + target[i], + currentOriginal, + compositeType + ); + target[i] = compositeType.replacePropertyValues( target[i], objects, session ); + } + } + copied[i] = target[i]; } } } From 6827581a38d68c366c4baa3fb5ed0742f58162f8 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Mon, 8 Jan 2024 18:37:10 +0100 Subject: [PATCH 026/321] HHH-17106 Fix ClassCastException when using length 1 named enum mapping --- .../mapping/basic/EnumSingleCharTest.java | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/EnumSingleCharTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/EnumSingleCharTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/EnumSingleCharTest.java new file mode 100644 index 000000000000..d311ecbaa60d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/EnumSingleCharTest.java @@ -0,0 +1,95 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.mapping.basic; + +import java.util.List; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@SessionFactory +@DomainModel(annotatedClasses = EnumSingleCharTest.Person.class) +@JiraKey("HHH-17106") +public class EnumSingleCharTest { + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.persist( new Person( 1L, SinglecharEnum.B ) ); + session.persist( new Person( 2L, SinglecharEnum.R ) ); + } ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createMutationQuery( "delete from Person" ).executeUpdate() ); + } + + @Test + public void testRead(SessionFactoryScope scope) { + scope.inTransaction( session -> { + List resultList = session.createQuery( "from Person order by id", Person.class ).getResultList(); + assertEquals( 2, resultList.size() ); + assertEquals( SinglecharEnum.B, resultList.get( 0 ).getSingleChar() ); + assertEquals( SinglecharEnum.R, resultList.get( 1 ).getSingleChar() ); + } ); + } + + public enum SinglecharEnum { + B("first"), + R("second"), + O("third"), + K("fourth"), + E("fifth"), + N("sixth"); + + private final String item; + + SinglecharEnum(String item) { + this.item = item; + } + + public String getItem() { + return item; + } + } + + @Entity(name = "Person") + public static class Person { + @Id + private Long id; + + @Column(name="SINGLE_CHAR", length = 1) + @Enumerated(EnumType.STRING) + private SinglecharEnum singleChar; + + public Person() { + } + + public Person(Long id, SinglecharEnum singleChar) { + this.id = id; + this.singleChar = singleChar; + } + + public SinglecharEnum getSingleChar() { + return singleChar; + } + } +} From b99b6e414653fce09eaadbc006300a503fc991d7 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 9 Jan 2024 11:14:16 +0100 Subject: [PATCH 027/321] HHH-17615 Fix pruning of soft delete table for joined inheritance --- .../entity/AbstractEntityPersister.java | 4 ++++ .../JoinedSubclassSoftDeleteTests.java | 20 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 598fad93bb7d..428429cefb7e 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -3117,6 +3117,10 @@ public TableGroup createRootTableGroup( creationState.getSqlExpressionResolver() ); additionalPredicateCollectorAccess.get().accept( softDeletePredicate ); + if ( tableReference != rootTableReference && creationState.supportsEntityNameUsage() ) { + // Register entity name usage for the hierarchy root table to avoid pruning + creationState.registerEntityNameUsage( tableGroup, EntityNameUse.EXPRESSION, getRootEntityName() ); + } } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/softdelete/secondary/JoinedSubclassSoftDeleteTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/softdelete/secondary/JoinedSubclassSoftDeleteTests.java index c60632becf24..5a54cd0b18cf 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/softdelete/secondary/JoinedSubclassSoftDeleteTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/softdelete/secondary/JoinedSubclassSoftDeleteTests.java @@ -12,6 +12,7 @@ import org.hibernate.ObjectNotFoundException; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.AfterEach; @@ -55,11 +56,28 @@ void testSelectionQuery(SessionFactoryScope scope) { scope.inTransaction( (session) -> { // should not return #1 assertThat( session.createQuery( "from JoinedRoot" ).list() ).hasSize( 2 ); + assertThat( session.createQuery( "from JoinedRoot where id = 1" ).list() ).isEmpty(); } ); scope.inTransaction( (session) -> { // should not return #1 - assertThat( session.createQuery( "from JoinedSub" ).list() ).hasSize( 2 ); + assertThat( session.createQuery( "from JoinedSub where id = 1" ).list() ).isEmpty(); + } ); + } + + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-17615" ) + void testCountQuery(SessionFactoryScope scope) { + scope.inTransaction( (session) -> { + // should not return #1 + assertThat( session.createQuery( "select count(*) from JoinedRoot" ).uniqueResult() ).isEqualTo( 2L ); + assertThat( session.createQuery( "select count(*) from JoinedRoot where id = 1" ).uniqueResult() ).isEqualTo( 0L ); + } ); + + scope.inTransaction( (session) -> { + // should not return #1 + assertThat( session.createQuery( "select count(*) from JoinedSub" ).uniqueResult() ).isEqualTo( 2L ); + assertThat( session.createQuery( "select count(*) from JoinedSub where id = 1" ).uniqueResult() ).isEqualTo( 0L ); } ); } From c7658e1cab7e374d2e20515b54f01fe52936494d Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 9 Jan 2024 11:26:36 +0100 Subject: [PATCH 028/321] HHH-17615 Small fix to entity joins with soft-delete and inheritance --- .../query/sqm/sql/BaseSqmToSqlAstConverter.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index b6ba343088f0..6adf54959f27 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -12,7 +12,6 @@ import org.hibernate.HibernateException; import org.hibernate.Internal; import org.hibernate.LockMode; -import org.hibernate.boot.model.internal.SoftDeleteHelper; import org.hibernate.boot.model.process.internal.InferredBasicValueResolver; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.DmlTargetColumnQualifierSupport; @@ -31,7 +30,6 @@ import org.hibernate.id.OptimizableGenerator; import org.hibernate.id.enhanced.Optimizer; import org.hibernate.internal.FilterHelper; -import org.hibernate.internal.util.MutableBoolean; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.Stack; import org.hibernate.internal.util.collections.StandardStack; @@ -422,6 +420,7 @@ import java.util.function.Supplier; import static java.util.Collections.singletonList; +import static org.hibernate.boot.model.internal.SoftDeleteHelper.createNonSoftDeletedRestriction; import static org.hibernate.generator.EventType.INSERT; import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues; import static org.hibernate.query.sqm.BinaryArithmeticOperator.ADD; @@ -3441,7 +3440,6 @@ private TableGroup consumeCrossJoin(SqmCrossJoin sqmJoin, TableGroup lhsTable } private TableGroup consumeEntityJoin(SqmEntityJoin sqmJoin, TableGroup lhsTableGroup, boolean transitive) { - final MutableBoolean needsTreat = new MutableBoolean( false ); final EntityPersister entityDescriptor = resolveEntityPersister( sqmJoin.getReferencedPathSource() ); final SqlAstJoinType correspondingSqlJoinType = sqmJoin.getSqmJoinType().getCorrespondingSqlJoinType(); @@ -3450,12 +3448,12 @@ private TableGroup consumeEntityJoin(SqmEntityJoin sqmJoin, TableGroup lhsTab sqmJoin.getNavigablePath(), sqmJoin.getExplicitAlias(), null, - () -> p -> needsTreat.setValue( true ), + () -> p -> {}, this ); registerSqmFromTableGroup( sqmJoin, tableGroup ); - if ( needsTreat.getValue() ) { + if ( entityDescriptor.isInherited() && !sqmJoin.hasTreats() ) { // Register new treat to apply the discriminator condition to the table reference itself, see #pruneTableGroupJoins registerEntityNameUsage( tableGroup, EntityNameUse.TREAT, entityDescriptor.getEntityName() ); } @@ -3479,8 +3477,8 @@ private TableGroup consumeEntityJoin(SqmEntityJoin sqmJoin, TableGroup lhsTab final SoftDeleteMapping softDeleteMapping = entityDescriptor.getSoftDeleteMapping(); if ( softDeleteMapping != null ) { - final Predicate softDeleteRestriction = SoftDeleteHelper.createNonSoftDeletedRestriction( - tableGroup.resolveTableReference( entityDescriptor.getSoftDeleteTableDetails().getTableName() ), + final Predicate softDeleteRestriction = createNonSoftDeletedRestriction( + tableGroup.resolveTableReference( softDeleteMapping.getTableName() ), softDeleteMapping ); tableGroupJoin.applyPredicate( softDeleteRestriction ); From bc1210e6412189d810f8ef9e08506de12d3c0ef7 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Thu, 4 Jan 2024 11:55:06 +0100 Subject: [PATCH 029/321] HHH-17435 Add test for issue --- .../orm/test/query/hql/FunctionTests.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java index 5da622ce150a..bf9264392b11 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java @@ -19,6 +19,8 @@ import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.SybaseDialect; import org.hibernate.dialect.TiDBDialect; +import org.hibernate.query.sqm.produce.function.FunctionArgumentException; + import org.hibernate.testing.TestForIssue; import org.hibernate.testing.orm.domain.StandardDomainModel; import org.hibernate.testing.orm.domain.gambit.EntityOfBasics; @@ -829,6 +831,52 @@ public void testTrimFunction(SessionFactoryScope scope) { } } + @Test + @JiraKey( "HHH-17435" ) + public void testTrimFunctionParameters(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + assertThat( session.createQuery( "select trim(:param)", String.class ) + .setParameter( "param", " hello " ) + .getSingleResult(), is( "hello" ) ); + assertThat( session.createQuery( "select trim(' ' from :param)", String.class ) + .setParameter( "param", " hello " ) + .getSingleResult(), is( "hello" ) ); + assertThat( session.createQuery( "select trim('''' from :param)", String.class ) + .setParameter( "param", "''hello'''" ) + .getSingleResult(), is( "hello" ) ); + assertThat( session.createQuery( "select trim(:param from '-- hello it''s me ---')", String.class ) + .setParameter( "param", '-' ) + .getSingleResult(), is( " hello it's me " ) ); + assertThat( session.createQuery( "select trim(:param from '--- hello it''s me -- ')", String.class ) + .setParameter( "param", '-' ) + .getSingleResult(), is( " hello it's me -- " ) ); + assertThat( session.createQuery( "select trim(leading ?1 from ' hello it''s me ')", String.class ) + .setParameter( 1, ' ' ) + .getSingleResult(), is( "hello it's me " ) ); + assertThat( session.createQuery( "select trim(trailing ?1 from ' hello it''s me ')", String.class ) + .setParameter( 1, ' ' ) + .getSingleResult(), is( " hello it's me" ) ); + assertThat( session.createQuery( "select trim(?1 from ?2)", String.class ) + .setParameter( 1, ' ' ) + .setParameter( 2, " hello it's me " ) + .getSingleResult(), is( "hello it's me" ) ); + } + ); + + try { + scope.inTransaction( + session -> session.createQuery( "select trim(:param from 'hello')", String.class ) + .setParameter( "param", 1 ) + .getResultList() + ); + fail(); + } + catch (IllegalArgumentException e) { + assertThat( e.getCause(), is( instanceOf( FunctionArgumentException.class ) ) ); + } + } + @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsPadWithChar.class) public void testPadFunction(SessionFactoryScope scope) { From 8daa7ced493193d4d8608241c31c9c5dd123d119 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Thu, 4 Jan 2024 11:55:23 +0100 Subject: [PATCH 030/321] HHH-17435 Allow input param as trim character and fix single quote --- .../community/dialect/AltibaseDialect.java | 16 +++--- .../community/dialect/DB2LegacyDialect.java | 8 +++ .../community/dialect/HSQLLegacyDialect.java | 8 +++ .../community/dialect/MaxDBDialect.java | 4 +- .../community/dialect/RDMSOS2200Dialect.java | 4 +- .../dialect/SQLServerLegacyDialect.java | 16 +++--- .../community/dialect/SQLiteDialect.java | 14 ++--- .../dialect/SybaseLegacyDialect.java | 6 -- .../org/hibernate/grammars/hql/HqlParser.g4 | 1 + .../dialect/AbstractTransactSQLDialect.java | 28 ++++++---- .../org/hibernate/dialect/DB2Dialect.java | 8 +++ .../java/org/hibernate/dialect/Dialect.java | 24 +++++++- .../dialect/DialectDelegateWrapper.java | 5 ++ .../org/hibernate/dialect/HSQLDialect.java | 8 +++ .../hibernate/dialect/SQLServerDialect.java | 16 +++--- .../org/hibernate/dialect/SybaseDialect.java | 6 -- .../dialect/function/TrimFunction.java | 55 ++++++++++++++++--- .../hql/internal/SemanticQueryBuilder.java | 28 +++++++--- .../AnsiTrimEmulationFunctionTest.java | 28 +++++----- 19 files changed, 188 insertions(+), 95 deletions(-) diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/AltibaseDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/AltibaseDialect.java index 62fda8d2b1c6..ecc99eaa997b 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/AltibaseDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/AltibaseDialect.java @@ -138,23 +138,23 @@ public int getDefaultStatementBatchSize() { } @Override - public String trimPattern(TrimSpec specification, char character) { + public String trimPattern(TrimSpec specification, boolean isWhitespace) { switch ( specification ) { case BOTH: - return character == ' ' + return isWhitespace ? "trim(?1)" - : "trim(?1, '" + character + "')"; + : "trim(?1,?2)"; case LEADING: - return character == ' ' + return isWhitespace ? "ltrim(?1)" - : "ltrim(?1,'" + character + "')"; + : "ltrim(?1,?2)"; case TRAILING: - return character == ' ' + return isWhitespace ? "rtrim(?1)" - : "rtrim(?1,'" + character + "')"; + : "rtrim(?1,?2)"; } - return super.trimPattern( specification, character ); + return super.trimPattern( specification, isWhitespace ); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java index 6ae54dfdd5c3..f82544d1b8d4 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java @@ -33,6 +33,7 @@ import org.hibernate.dialect.function.DB2FormatEmulation; import org.hibernate.dialect.function.DB2PositionFunction; import org.hibernate.dialect.function.DB2SubstringFunction; +import org.hibernate.dialect.function.TrimFunction; import org.hibernate.dialect.identity.DB2IdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.pagination.DB2LimitHandler; @@ -398,6 +399,13 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio .setArgumentListSignature("(STRING string, STRING pattern)") .register(); + //trim() requires trim characters to be constant literals + functionContributions.getFunctionRegistry().register( "trim", new TrimFunction( + this, + functionContributions.getTypeConfiguration(), + SqlAstNodeRenderingMode.INLINE_PARAMETERS + ) ); + functionFactory.windowFunctions(); if ( getDB2Version().isSameOrAfter( 9, 5 ) ) { functionFactory.listagg( null ); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java index 7d196ca37f07..6a5e4f8f908e 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java @@ -16,6 +16,7 @@ import org.hibernate.boot.model.FunctionContributions; import org.hibernate.dialect.*; import org.hibernate.dialect.function.CommonFunctionFactory; +import org.hibernate.dialect.function.TrimFunction; import org.hibernate.dialect.identity.HSQLIdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.lock.LockingStrategy; @@ -266,6 +267,13 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.arrayTrim_trim_array(); functionFactory.arrayFill_hsql(); functionFactory.arrayToString_hsql(); + + //trim() requires parameters to be cast when used as trim character + functionContributions.getFunctionRegistry().register( "trim", new TrimFunction( + this, + functionContributions.getTypeConfiguration(), + SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER + ) ); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MaxDBDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MaxDBDialect.java index fe2ee0fa2746..e527bd5baafa 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MaxDBDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MaxDBDialect.java @@ -203,8 +203,8 @@ protected SqlAstTranslator buildTranslator( } @Override - public String trimPattern(TrimSpec specification, char character) { - return AbstractTransactSQLDialect.replaceLtrimRtrim( specification, character); + public String trimPattern(TrimSpec specification, boolean isWhitespace) { + return AbstractTransactSQLDialect.replaceLtrimRtrim( specification, isWhitespace ); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/RDMSOS2200Dialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/RDMSOS2200Dialect.java index 3bdea363ea3d..b4b111aa52a1 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/RDMSOS2200Dialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/RDMSOS2200Dialect.java @@ -431,7 +431,7 @@ public void appendDatetimeFormat(SqlAppender appender, String format) { } @Override - public String trimPattern(TrimSpec specification, char character) { - return AbstractTransactSQLDialect.replaceLtrimRtrim( specification, character); + public String trimPattern(TrimSpec specification, boolean isWhitespace) { + return AbstractTransactSQLDialect.replaceLtrimRtrim( specification, isWhitespace ); } } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacyDialect.java index b3f0440b4b2b..b6bbaf05289d 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacyDialect.java @@ -385,25 +385,25 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio } @Override - public String trimPattern(TrimSpec specification, char character) { + public String trimPattern(TrimSpec specification, boolean isWhitespace) { if ( getVersion().isSameOrAfter( 16 ) ) { switch ( specification ) { case BOTH: - return character == ' ' + return isWhitespace ? "trim(?1)" - : "trim('" + character + "' from ?1)"; + : "trim(?2 from ?1)"; case LEADING: - return character == ' ' + return isWhitespace ? "ltrim(?1)" - : "ltrim(?1,'" + character + "')"; + : "ltrim(?1,?2)"; case TRAILING: - return character == ' ' + return isWhitespace ? "rtrim(?1)" - : "rtrim(?1,'" + character + "')"; + : "rtrim(?1,?2)"; } throw new UnsupportedOperationException( "Unsupported specification: " + specification ); } - return super.trimPattern( specification, character ); + return super.trimPattern( specification, isWhitespace ); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLiteDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLiteDialect.java index 0f66ac33562d..65725af6419c 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLiteDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLiteDialect.java @@ -353,20 +353,20 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio } @Override - public String trimPattern(TrimSpec specification, char character) { + public String trimPattern(TrimSpec specification, boolean isWhitespace) { switch ( specification ) { case BOTH: - return character == ' ' + return isWhitespace ? "trim(?1)" - : "trim(?1,'" + character + "')"; + : "trim(?1,?2)"; case LEADING: - return character == ' ' + return isWhitespace ? "ltrim(?1)" - : "ltrim(?1,'" + character + "')"; + : "ltrim(?1,?2)"; case TRAILING: - return character == ' ' + return isWhitespace ? "rtrim(?1)" - : "rtrim(?1,'" + character + "')"; + : "rtrim(?1,?2)"; } throw new UnsupportedOperationException( "Unsupported specification: " + specification ); } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseLegacyDialect.java index 2b35a5c10eb5..f47097a76c0d 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseLegacyDialect.java @@ -424,12 +424,6 @@ public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalT return "datediff(?1,?2,?3)"; } - @Override - public String trimPattern(TrimSpec specification, char character) { - return super.trimPattern(specification, character) - .replace("replace", "str_replace"); - } - @Override public void appendDatetimeFormat(SqlAppender appender, String format) { throw new UnsupportedOperationException( "format() function not supported on Sybase"); diff --git a/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 b/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 index 273f8cdb2822..224fba7d99dc 100644 --- a/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 +++ b/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 @@ -1357,6 +1357,7 @@ trimSpecification trimCharacter : STRING_LITERAL + | parameter ; /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java index ba2a49f1a65d..2b6e5252b91b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java @@ -166,28 +166,32 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio } @Override - public String trimPattern(TrimSpec specification, char character) { - return replaceLtrimRtrim(specification, character); + public String trimPattern(TrimSpec specification, boolean isWhitespace) { + return replaceLtrimRtrim( specification, isWhitespace ); } + /** + * @deprecated Use {@link #replaceLtrimRtrim(TrimSpec, boolean)} instead. + */ + @Deprecated( forRemoval = true ) public static String replaceLtrimRtrim(TrimSpec specification, char character) { - boolean blank = character == ' '; + return replaceLtrimRtrim( specification, character == ' ' ); + } + + public static String replaceLtrimRtrim(TrimSpec specification, boolean isWhitespace) { switch ( specification ) { case LEADING: - return blank + return isWhitespace ? "ltrim(?1)" - : "replace(replace(ltrim(replace(replace(?1,' ','#%#%'),'@',' ')),' ','@'),'#%#%',' ')" - .replace('@', character); + : "substring(?1,patindex('%[^'+?2+']%',?1),len(?1)-patindex('%[^'+?2+']%',?1)+1)"; case TRAILING: - return blank + return isWhitespace ? "rtrim(?1)" - : "replace(replace(rtrim(replace(replace(?1,' ','#%#%'),'@',' ')),' ','@'),'#%#%',' ')" - .replace('@', character); + : "substring(?1,1,len(?1)-patindex('%[^'+?2+']%',reverse(?1))+1)"; default: - return blank + return isWhitespace ? "ltrim(rtrim(?1))" - : "replace(replace(ltrim(rtrim(replace(replace(?1,' ','#%#%'),'@',' '))),' ','@'),'#%#%',' ')" - .replace('@', character); + : "substring(?1,patindex('%[^'+?2+']%',?1),len(?1)-patindex('%[^'+?2+']%',?1)-patindex('%[^'+?2+']%',reverse(?1))+2)"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java index a8aba81b1d35..afd609d1b07e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -28,6 +28,7 @@ import org.hibernate.dialect.function.DB2FormatEmulation; import org.hibernate.dialect.function.DB2PositionFunction; import org.hibernate.dialect.function.DB2SubstringFunction; +import org.hibernate.dialect.function.TrimFunction; import org.hibernate.dialect.identity.DB2IdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.pagination.DB2LimitHandler; @@ -387,6 +388,13 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio .setArgumentListSignature("(STRING string, STRING pattern)") .register(); + //trim() requires trim characters to be constant literals + functionContributions.getFunctionRegistry().register( "trim", new TrimFunction( + this, + functionContributions.getTypeConfiguration(), + SqlAstNodeRenderingMode.INLINE_PARAMETERS + ) ); + functionFactory.windowFunctions(); functionFactory.listagg( null ); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index d37c7972360f..82d74c52eece 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -1484,11 +1484,29 @@ public String castPattern(CastType from, CastType to) { * * @param specification {@code leading} or {@code trailing} * @param character the character to trim + * + * @deprecated Use {@link #trimPattern(TrimSpec, boolean)} instead. */ + @Deprecated( forRemoval = true ) public String trimPattern(TrimSpec specification, char character) { - return character == ' ' - ? "trim(" + specification + " from ?1)" - : "trim(" + specification + " '" + character + "' from ?1)"; + return trimPattern( specification, character == ' ' ); + } + + /** + * Obtain a pattern for the SQL equivalent to a + * {@code trim()} function call. The resulting + * pattern must contain a ?1 placeholder for the + * argument of type {@link String} and a ?2 placeholder + * for the trim character if {@code isWhitespace} + * was false. + * + * @param specification {@linkplain TrimSpec#LEADING leading}, {@linkplain TrimSpec#TRAILING trailing} + * or {@linkplain TrimSpec#BOTH both} + * @param isWhitespace {@code true} if the trim character is a whitespace and can be omitted, + * {@code false} if it must be explicit and a ?2 placeholder should be included in the pattern + */ + public String trimPattern(TrimSpec specification, boolean isWhitespace) { + return "trim(" + specification + ( isWhitespace ? "" : " ?2" ) + " from ?1)"; } /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java b/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java index 64befb71dab5..a6370d76aa59 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java @@ -290,6 +290,11 @@ public String trimPattern(TrimSpec specification, char character) { return wrapped.trimPattern( specification, character ); } + @Override + public String trimPattern(TrimSpec specification, boolean isWhitespace) { + return wrapped.trimPattern( specification, isWhitespace ); + } + @Override public boolean supportsFractionalTimestampArithmetic() { return wrapped.supportsFractionalTimestampArithmetic(); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java index 94a5a84ad881..8c86b32c9c39 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java @@ -12,6 +12,7 @@ import org.hibernate.boot.model.FunctionContributions; import org.hibernate.dialect.function.CommonFunctionFactory; +import org.hibernate.dialect.function.TrimFunction; import org.hibernate.dialect.identity.HSQLIdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.pagination.LimitHandler; @@ -206,6 +207,13 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.arrayTrim_trim_array(); functionFactory.arrayFill_hsql(); functionFactory.arrayToString_hsql(); + + //trim() requires parameters to be cast when used as trim character + functionContributions.getFunctionRegistry().register( "trim", new TrimFunction( + this, + functionContributions.getTypeConfiguration(), + SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER + ) ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java index 36f2aac6021d..00f46aa079bf 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java @@ -396,25 +396,25 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio } @Override - public String trimPattern(TrimSpec specification, char character) { + public String trimPattern(TrimSpec specification, boolean isWhitespace) { if ( getVersion().isSameOrAfter( 16 ) ) { switch ( specification ) { case BOTH: - return character == ' ' + return isWhitespace ? "trim(?1)" - : "trim('" + character + "' from ?1)"; + : "trim(?2 from ?1)"; case LEADING: - return character == ' ' + return isWhitespace ? "ltrim(?1)" - : "ltrim(?1,'" + character + "')"; + : "ltrim(?1,?2)"; case TRAILING: - return character == ' ' + return isWhitespace ? "rtrim(?1)" - : "rtrim(?1,'" + character + "')"; + : "rtrim(?1,?2)"; } throw new UnsupportedOperationException( "Unsupported specification: " + specification ); } - return super.trimPattern( specification, character ); + return super.trimPattern( specification, isWhitespace ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java index fbc236b31154..348259e3fe65 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java @@ -447,12 +447,6 @@ public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalT return "datediff(?1,?2,?3)"; } - @Override - public String trimPattern(TrimSpec specification, char character) { - return super.trimPattern(specification, character) - .replace("replace", "str_replace"); - } - @Override public void appendDatetimeFormat(SqlAppender appender, String format) { throw new UnsupportedOperationException( "format() function not supported on Sybase"); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/TrimFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/TrimFunction.java index fa44b7ba6951..1f39f70b7260 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/TrimFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/TrimFunction.java @@ -6,44 +6,56 @@ */ package org.hibernate.dialect.function; +import java.util.Collections; +import java.util.List; + import org.hibernate.dialect.Dialect; import org.hibernate.query.ReturnableType; import org.hibernate.query.sqm.TrimSpec; import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor; import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator; +import org.hibernate.query.sqm.produce.function.FunctionArgumentException; import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators; import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers; import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers; import org.hibernate.query.sqm.produce.function.internal.PatternRenderer; +import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation; +import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.TrimSpecification; +import org.hibernate.type.SqlTypes; import org.hibernate.type.StandardBasicTypes; +import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.spi.TypeConfiguration; -import java.util.Collections; -import java.util.List; - -import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TRIM_SPEC; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING; +import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TRIM_SPEC; /** * ANSI SQL-standard {@code trim()} function, which has a funny syntax * involving a {@link TrimSpec}, and portability is achieved using - * {@link Dialect#trimPattern(TrimSpec, char)}. + * {@link Dialect#trimPattern(TrimSpec, boolean)}. *

* For example, {@code trim(leading ' ' from text)}. * * @author Gavin King */ public class TrimFunction extends AbstractSqmSelfRenderingFunctionDescriptor { - private final Dialect dialect; + private SqlAstNodeRenderingMode argumentRenderingMode; public TrimFunction(Dialect dialect, TypeConfiguration typeConfiguration) { + this( dialect, typeConfiguration, SqlAstNodeRenderingMode.DEFAULT ); + } + + public TrimFunction( + Dialect dialect, + TypeConfiguration typeConfiguration, + SqlAstNodeRenderingMode argumentRenderingMode) { super( "trim", new ArgumentTypesValidator( @@ -56,6 +68,7 @@ public TrimFunction(Dialect dialect, TypeConfiguration typeConfiguration) { StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, TRIM_SPEC, STRING, STRING ) ); this.dialect = dialect; + this.argumentRenderingMode = argumentRenderingMode; } @Override @@ -65,12 +78,36 @@ public void render( ReturnableType returnType, SqlAstTranslator walker) { final TrimSpec specification = ( (TrimSpecification) sqlAstArguments.get( 0 ) ).getSpecification(); - final Object trimCharacter = ( (Literal) sqlAstArguments.get( 1 ) ).getLiteralValue(); + final SqlAstNode trimCharacter = sqlAstArguments.get( 1 ); + final boolean isWhitespace = isWhitespace( trimCharacter ); final Expression sourceExpr = (Expression) sqlAstArguments.get( 2 ); - String trim = dialect.trimPattern( specification, (char) trimCharacter ); + final String trim = dialect.trimPattern( specification, isWhitespace ); + + final List args = isWhitespace ? + Collections.singletonList( sourceExpr ) : + List.of( sourceExpr, trimCharacter ); + new PatternRenderer( trim, argumentRenderingMode ).render( sqlAppender, args, walker ); + } - new PatternRenderer( trim ).render( sqlAppender, Collections.singletonList( sourceExpr ), walker ); + private static boolean isWhitespace(SqlAstNode trimCharacter) { + if ( trimCharacter instanceof Literal ) { + final char literalValue = (char) ( (Literal) trimCharacter ).getLiteralValue(); + return literalValue == ' '; + } + else { + assert trimCharacter instanceof SqmParameterInterpretation; + final JdbcType jdbcType = ( (SqmParameterInterpretation) trimCharacter ).getExpressionType() + .getSingleJdbcMapping() + .getJdbcType(); + if ( jdbcType.getJdbcTypeCode() != SqlTypes.CHAR ) { + throw new FunctionArgumentException( String.format( + "Expected parameter used as trim character to be Character typed, instead was [%s]", + jdbcType.getFriendlyName() + ) ); + } + return false; + } } // @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java index 5d7f628e4bc4..f9563ca79b5c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java @@ -4724,7 +4724,7 @@ public SqmLiteral visitPadCharacter(HqlParser.PadCharacterContext ctx public SqmExpression visitTrimFunction(HqlParser.TrimFunctionContext ctx) { final SqmExpression source = (SqmExpression) ctx.expression().accept( this ); final SqmTrimSpecification trimSpec = visitTrimSpecification( ctx.trimSpecification() );; - final SqmLiteral trimChar = visitTrimCharacter( ctx.trimCharacter() ); + final SqmExpression trimChar = visitTrimCharacter( ctx.trimCharacter() ); return getFunctionDescriptor("trim").generateSqmExpression( asList( @@ -4758,15 +4758,25 @@ private static TrimSpec trimSpec(HqlParser.TrimSpecificationContext ctx) { } @Override - public SqmLiteral visitTrimCharacter(HqlParser.TrimCharacterContext ctx) { - final String trimCharText = ctx != null - ? unquoteStringLiteral( ctx.getText() ) - : " "; // JPA says space is the default - - if ( trimCharText.length() != 1 ) { - throw new SemanticException( "Trim character for trim() function must be single character, found '" + trimCharText + "'" ); + public SqmExpression visitTrimCharacter(HqlParser.TrimCharacterContext ctx) { + final String trimCharText; + if ( ctx == null ) { + // JPA says space is the default + trimCharText = " "; + } + else { + final ParseTree child = ctx.getChild( 0 ); + if ( child instanceof HqlParser.ParameterContext ) { + //noinspection unchecked + return (SqmExpression) child.accept( this ); + } + else { + trimCharText = unquoteStringLiteral( ctx.getText() ); + if ( trimCharText.length() != 1 ) { + throw new SemanticException( "Trim character for trim() function must be single character, found '" + trimCharText + "'" ); + } + } } - return new SqmLiteral<>( trimCharText.charAt( 0 ), resolveExpressibleTypeBasic( Character.class ), diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/function/AnsiTrimEmulationFunctionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/function/AnsiTrimEmulationFunctionTest.java index eec8198aecf7..bb895b1de995 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/function/AnsiTrimEmulationFunctionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/function/AnsiTrimEmulationFunctionTest.java @@ -32,6 +32,7 @@ import org.hibernate.type.internal.BasicTypeImpl; import org.hibernate.type.spi.TypeConfiguration; +import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.ServiceRegistry; import org.hibernate.testing.orm.junit.ServiceRegistryScope; import org.junit.jupiter.api.Test; @@ -41,65 +42,62 @@ import static org.junit.jupiter.api.Assertions.assertEquals; /** - * TODO : javadoc + * Tests correct rendering of trim function emulation for {@link org.hibernate.dialect.AbstractTransactSQLDialect} dialects. * * @author Christian Beikov */ @ServiceRegistry public class AnsiTrimEmulationFunctionTest { private static final String trimSource = "a.column"; + private static final String LEADING = "substring(?1,patindex('%[^'+?2+']%',?1),len(?1)-patindex('%[^'+?2+']%',?1)+1)"; + private static final String TRAILING = "substring(?1,1,len(?1)-patindex('%[^'+?2+']%',reverse(?1))+1)"; + private static final String BOTH = "substring(?1,patindex('%[^'+?2+']%',?1),len(?1)-patindex('%[^'+?2+']%',?1)-patindex('%[^'+?2+']%',reverse(?1))+2)"; @Test + @RequiresDialect( SQLServerDialect.class ) public void testBasicSqlServerProcessing(ServiceRegistryScope scope) { Dialect dialect = new SQLServerDialect(); TrimFunction function = new TrimFunction( dialect, new TypeConfiguration() ); performBasicSpaceTrimmingTests( dialect, scope.getRegistry(), function ); - final String expectedTrimPrep = "replace(replace(a.column,' ','#%#%'),'-',' ')"; - final String expectedPostTrimPrefix = "replace(replace("; - final String expectedPostTrimSuffix = ",' ','-'),'#%#%',' ')"; - // -> trim(LEADING '-' FROM a.column) String rendered = render( dialect, scope.getRegistry(), function, TrimSpec.LEADING, '-', trimSource ); - String expected = expectedPostTrimPrefix + "ltrim(" + expectedTrimPrep + ")" + expectedPostTrimSuffix; + String expected = LEADING.replace( "?1", trimSource ).replace( "?2", "'-'" ); assertEquals( expected, rendered ); // -> trim(TRAILING '-' FROM a.column) rendered = render( dialect, scope.getRegistry(), function, TrimSpec.TRAILING, '-', trimSource ); - expected = expectedPostTrimPrefix + "rtrim(" + expectedTrimPrep + ")" + expectedPostTrimSuffix; + expected = TRAILING.replace( "?1", trimSource ).replace( "?2", "'-'" ); assertEquals( expected, rendered ); // -> trim(BOTH '-' FROM a.column) rendered = render( dialect, scope.getRegistry(), function, TrimSpec.BOTH, '-', trimSource ); - expected = expectedPostTrimPrefix + "ltrim(rtrim(" + expectedTrimPrep + "))" + expectedPostTrimSuffix; + expected = BOTH.replace( "?1", trimSource ).replace( "?2", "'-'" ); assertEquals( expected, rendered ); } @Test + @RequiresDialect( SybaseDialect.class ) public void testBasicSybaseProcessing(ServiceRegistryScope scope) { Dialect dialect = new SybaseDialect(); TrimFunction function = new TrimFunction( dialect, new TypeConfiguration() ); performBasicSpaceTrimmingTests( dialect, scope.getRegistry(), function ); - final String expectedTrimPrep = "str_replace(str_replace(a.column,' ','#%#%'),'-',' ')"; - final String expectedPostTrimPrefix = "str_replace(str_replace("; - final String expectedPostTrimSuffix = ",' ','-'),'#%#%',' ')"; - // -> trim(LEADING '-' FROM a.column) String rendered = render( dialect, scope.getRegistry(), function, TrimSpec.LEADING, '-', trimSource ); - String expected = expectedPostTrimPrefix + "ltrim(" + expectedTrimPrep + ")" + expectedPostTrimSuffix; + String expected = LEADING.replace( "?1", trimSource ).replace( "?2", "'-'" ); assertEquals( expected, rendered ); // -> trim(TRAILING '-' FROM a.column) rendered = render( dialect, scope.getRegistry(), function, TrimSpec.TRAILING, '-', trimSource ); - expected = expectedPostTrimPrefix + "rtrim(" + expectedTrimPrep + ")" + expectedPostTrimSuffix; + expected = TRAILING.replace( "?1", trimSource ).replace( "?2", "'-'" ); assertEquals( expected, rendered ); // -> trim(BOTH '-' FROM a.column) rendered = render( dialect, scope.getRegistry(), function, TrimSpec.BOTH, '-', trimSource ); - expected = expectedPostTrimPrefix + "ltrim(rtrim(" + expectedTrimPrep + "))" + expectedPostTrimSuffix; + expected = BOTH.replace( "?1", trimSource ).replace( "?2", "'-'" ); assertEquals( expected, rendered ); } From dc2c2823ed911e9bdf2fafe9d19933e0bc70c5cd Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 6 Dec 2023 12:25:08 +0100 Subject: [PATCH 031/321] HHH-17492 Add test for issue --- .../query/hql/MultiValuedParameterTest.java | 46 +++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/MultiValuedParameterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/MultiValuedParameterTest.java index d83248dcc9fd..a1b1abf05112 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/MultiValuedParameterTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/MultiValuedParameterTest.java @@ -6,6 +6,7 @@ */ package org.hibernate.orm.test.query.hql; +import java.math.BigInteger; import java.time.LocalDate; import java.util.ArrayList; import java.util.Collection; @@ -14,27 +15,30 @@ import org.hibernate.boot.MetadataSources; import org.hibernate.query.Query; -import org.hibernate.testing.TestForIssue; import org.hibernate.testing.orm.domain.contacts.Contact; import org.hibernate.testing.orm.domain.contacts.ContactsDomainModel; import org.hibernate.testing.orm.junit.BaseSessionFactoryFunctionalTest; +import org.hibernate.testing.orm.junit.Jira; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; /** * @author Andrea Boriero */ -@TestForIssue(jiraKey = "HHH-10893") public class MultiValuedParameterTest extends BaseSessionFactoryFunctionalTest { @Override protected void applyMetadataSources(MetadataSources metadataSources) { super.applyMetadataSources( metadataSources ); ContactsDomainModel.applyContactsModel( metadataSources ); + metadataSources.addAnnotatedClass( EntityWithNumericId.class ); } @BeforeAll @@ -48,13 +52,17 @@ public void prepareData() { Contact.Gender.MALE, LocalDate.now() ); - session.save( p1 ); + session.persist( p1 ); + if ( i < 3 ) { + session.persist( new EntityWithNumericId( BigInteger.valueOf( i ) ) ); + } } } ); } @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-10893" ) public void testParameterListIn() { inTransaction( session -> { @@ -80,8 +88,38 @@ public void testParameterListIn() { ); } + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-17492" ) + public void test() { + inTransaction( session -> { + final List ids = List.of( BigInteger.ZERO, BigInteger.ONE, BigInteger.TWO ); + final List resultList = session.createQuery( + "select id from EntityWithNumericId e WHERE e.id in (:ids)", + EntityWithNumericId.class + ).setParameter( "ids", ids ).getResultList(); + assertThat( resultList.size(), is( 3 ) ); + assertThat( resultList, is( ids ) ); + } ); + } + @AfterAll public void cleanupData() { - inTransaction( session -> session.createQuery( "delete Contact" ).executeUpdate() ); + inTransaction( session -> { + session.createMutationQuery( "delete Contact" ).executeUpdate(); + session.createMutationQuery( "delete EntityWithNumericId" ).executeUpdate(); + } ); + } + + @Entity( name = "EntityWithNumericId" ) + public static class EntityWithNumericId { + @Id + private BigInteger id; + + public EntityWithNumericId() { + } + + public EntityWithNumericId(BigInteger id) { + this.id = id; + } } } From b8d95528031fa66e3180612e8371128607e1d435 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 6 Dec 2023 12:25:40 +0100 Subject: [PATCH 032/321] HHH-17492 Allow parameter inferred mapping for same java types --- .../org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 6adf54959f27..b79d20a4869e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -5930,7 +5930,8 @@ else if ( paramType instanceof MappingModelExpressible ) { final JdbcMapping paramJdbcMapping = paramModelType.getSingleJdbcMapping(); final JdbcMapping inferredJdbcMapping = inferredValueMapping.getSingleJdbcMapping(); // Only use the inferred mapping as parameter type when the JavaType accepts values of the bind type - if ( inferredJdbcMapping.getMappedJavaType().isWider( paramJdbcMapping.getMappedJavaType() ) + if ( ( inferredJdbcMapping.getMappedJavaType() == paramJdbcMapping.getMappedJavaType() + || inferredJdbcMapping.getMappedJavaType().isWider( paramJdbcMapping.getMappedJavaType() ) ) // and the bind type is not explicit or the bind type has the same JDBC type && ( !bindingTypeExplicit || canUseInferredType( paramJdbcMapping, inferredJdbcMapping ) ) ) { return resolveInferredValueMappingForParameter( inferredValueMapping ); From 8f3cc245ceaccb105152d00dfd098df5d5337873 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 10 Jan 2024 15:17:17 +0100 Subject: [PATCH 033/321] HHH-17632 Add test for issue --- .../update/JoinedInheritanceTest.java | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/update/JoinedInheritanceTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/update/JoinedInheritanceTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/update/JoinedInheritanceTest.java new file mode 100644 index 000000000000..5485d343467c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/update/JoinedInheritanceTest.java @@ -0,0 +1,184 @@ +package org.hibernate.orm.test.bytecode.enhancement.update; + +import java.util.List; + +import org.hibernate.annotations.Formula; + +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.hibernate.testing.orm.junit.JiraKey; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import jakarta.persistence.Basic; +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.Table; + +import static jakarta.persistence.FetchType.LAZY; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +@RunWith(BytecodeEnhancerRunner.class) +@JiraKey("HHH-17632") +public class JoinedInheritanceTest extends BaseNonConfigCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Plane.class, A320.class + }; + } + + @Before + public void setUp() { + inTransaction( + session -> { + A320 a320 = new A320( 1l, "Airbus A320", 101, true, "1.0" ); + session.persist( a320 ); + } + ); + } + + @After + public void tearDown() { + inTransaction( + session -> { + session.createMutationQuery( "delete from A320" ).executeUpdate(); + } + ); + } + + @Test + public void testUpdateField() { + inTransaction( + session -> { + A320 referenceById = session.getReference( A320.class, 1L ); + + referenceById.setSoftwareVersion( "2.0" ); + + session.flush(); + session.clear(); + + List airbusA320 = session.createQuery( + "select a from A320 a where a.description = 'Airbus A320'" ).getResultList(); + assertFalse( airbusA320.isEmpty() ); + assertEquals( 1, airbusA320.size() ); + A320 a320 = airbusA320.get( 0 ); + assertEquals( "2.0", a320.getSoftwareVersion() ); + assertTrue( a320.getLarge() ); + } + ); + } + + @Test + public void testUpdateTwoFields() { + inTransaction( + session -> { + A320 referenceById = session.getReference( A320.class, 1L ); + + referenceById.setSoftwareVersion( "2.0" ); + referenceById.setNbrOfSeats( 103 ); + + session.flush(); + session.clear(); + + List airbusA320 = session.createQuery( + "select a from A320 a where a.description like 'Airbus A320'" ).getResultList(); + assertFalse( airbusA320.isEmpty() ); + assertEquals( 1, airbusA320.size() ); + A320 a320 = airbusA320.get( 0 ); + assertEquals( "2.0", a320.getSoftwareVersion() ); + assertEquals( 103, a320.getNbrOfSeats() ); + assertTrue( a320.getLarge() ); + } + ); + } + + + @Entity(name = "A320") + @Table(name = "a320") + public static class A320 extends Plane { + + @Column(name = "software_version") + private String softwareVersion; + + public A320() { + super(); + } + + public A320(Long id, String description, int nbrOfSeats, Boolean large, String softwareVersion) { + super( id, description, nbrOfSeats, large ); + this.softwareVersion = softwareVersion; + } + + public String getSoftwareVersion() { + return softwareVersion; + } + + public void setSoftwareVersion(String softwareVersion) { + this.softwareVersion = softwareVersion; + } + } + + @Table(name = "plane_table") + @Inheritance(strategy = InheritanceType.JOINED) + @DiscriminatorColumn + @Entity(name = "Plane") + public static class Plane { + + @Id + private Long id; + + @Column + private String description; + + @Column(name = "nbr_of_seats") + private int nbrOfSeats; + + @Basic(fetch = LAZY) + private Boolean large = false; + + public Plane() { + } + + public Plane(Long id, String description, int nbrOfSeats, Boolean large) { + this.id = id; + this.description = description; + this.nbrOfSeats = nbrOfSeats; + this.large = large; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public int getNbrOfSeats() { + return nbrOfSeats; + } + + public void setNbrOfSeats(int nbrOfSeats) { + this.nbrOfSeats = nbrOfSeats; + } + + public Boolean getLarge() { + return large; + } + + public void setLarge(Boolean large) { + this.large = large; + } + } +} From 899bf7b4fb3679fa9579a091888d6a2d50a8a5b8 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 10 Jan 2024 15:19:14 +0100 Subject: [PATCH 034/321] HHH-17632 AssertionError when updating entity with lazy loading property and bytecode enhancement --- .../persister/entity/mutation/UpdateCoordinatorStandard.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java index 576a9b29e4b8..29ee5ac2d5e9 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java @@ -1093,7 +1093,7 @@ private void applyTableUpdateDetails( final AttributeAnalysis attributeAnalysis = updateValuesAnalysis.attributeAnalyses.get( attributeIndex ); if ( attributeAnalysis.includeInSet() ) { - assert updateValuesAnalysis.tablesNeedingUpdate.contains( tableMapping ); + assert updateValuesAnalysis.tablesNeedingUpdate.contains( tableMapping ) || updateValuesAnalysis.tablesNeedingDynamicUpdate.contains( tableMapping ); applyAttributeUpdateDetails( entity, updateGroupBuilder, From 8250f13d77c931059890af328023b29c33b95660 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 12 Jan 2024 12:36:02 +0100 Subject: [PATCH 035/321] HHH-17639 Make recursive CTE cycle detection emulation independent of collation --- .../MariaDBLegacySqlAstTranslator.java | 9 ++++++ .../dialect/MySQLLegacySqlAstTranslator.java | 9 ++++++ .../SQLServerLegacySqlAstTranslator.java | 11 +++++++ .../dialect/MariaDBSqlAstTranslator.java | 9 ++++++ .../dialect/MySQLSqlAstTranslator.java | 9 ++++++ .../dialect/SQLServerSqlAstTranslator.java | 13 ++++++++ .../dialect/TiDBSqlAstTranslator.java | 9 ++++++ .../sql/ast/spi/AbstractSqlAstTranslator.java | 32 ++++++++++++++----- 8 files changed, 93 insertions(+), 8 deletions(-) diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacySqlAstTranslator.java index cc4b556692a1..df2c7dd0b7cb 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacySqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacySqlAstTranslator.java @@ -250,4 +250,13 @@ public void visitCastTarget(CastTarget castTarget) { super.visitCastTarget( castTarget ); } } + + @Override + protected void renderStringContainsExactlyPredicate(Expression haystack, Expression needle) { + // MariaDB can't cope with NUL characters in the position function, so we use a like predicate instead + haystack.accept( this ); + appendSql( " like concat('%',replace(replace(replace(" ); + needle.accept( this ); + appendSql( ",'~','~~'),'?','~?'),'%','~%'),'%') escape '~'" ); + } } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacySqlAstTranslator.java index 59ed599c9b04..972543693b77 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacySqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacySqlAstTranslator.java @@ -258,4 +258,13 @@ public void visitCastTarget(CastTarget castTarget) { super.visitCastTarget( castTarget ); } } + + @Override + protected void renderStringContainsExactlyPredicate(Expression haystack, Expression needle) { + // MySQL can't cope with NUL characters in the position function, so we use a like predicate instead + haystack.accept( this ); + appendSql( " like concat('%',replace(replace(replace(" ); + needle.accept( this ); + appendSql( ",'~','~~'),'?','~?'),'%','~%'),'%') escape '~'" ); + } } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacySqlAstTranslator.java index fe2d2d5cd582..a01d72ee2198 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacySqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacySqlAstTranslator.java @@ -470,4 +470,15 @@ enum OffsetFetchClauseMode { TOP_ONLY, EMULATED; } + + @Override + protected void renderStringContainsExactlyPredicate(Expression haystack, Expression needle) { + // SQL Server ignores NUL characters in string on case-insensitive collations, so we force a binary collation. + // This is needed for the emulation of cycle detection in recursive queries + appendSql( "charindex(" ); + needle.accept( this ); + appendSql( " collate Latin1_General_100_BIN2," ); + haystack.accept( this ); + append( ")>0" ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java index ac611c6dbadc..bd157b3250a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java @@ -240,4 +240,13 @@ public void visitCastTarget(CastTarget castTarget) { } } + @Override + protected void renderStringContainsExactlyPredicate(Expression haystack, Expression needle) { + // MariaDB can't cope with NUL characters in the position function, so we use a like predicate instead + haystack.accept( this ); + appendSql( " like concat('%',replace(replace(replace(" ); + needle.accept( this ); + appendSql( ",'~','~~'),'?','~?'),'%','~%'),'%') escape '~'" ); + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java index 5c5059fcb815..b4cd77c4ea8c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java @@ -294,4 +294,13 @@ public void visitCastTarget(CastTarget castTarget) { super.visitCastTarget( castTarget ); } } + + @Override + protected void renderStringContainsExactlyPredicate(Expression haystack, Expression needle) { + // MySQL can't cope with NUL characters in the position function, so we use a like predicate instead + haystack.accept( this ); + appendSql( " like concat('%',replace(replace(replace(" ); + needle.accept( this ); + appendSql( ",'~','~~'),'?','~?'),'%','~%'),'%') escape '~'" ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java index 9fae16a62794..c273829c7e0a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java @@ -14,6 +14,8 @@ import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.FetchClauseType; +import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor; +import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression; import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.spi.SqlSelection; @@ -451,4 +453,15 @@ protected void renderMergeStatement(OptionalTableUpdate optionalTableUpdate) { super.renderMergeStatement( optionalTableUpdate ); appendSql( ";" ); } + + @Override + protected void renderStringContainsExactlyPredicate(Expression haystack, Expression needle) { + // SQL Server ignores NUL characters in string on case-insensitive collations, so we force a binary collation. + // This is needed for the emulation of cycle detection in recursive queries + appendSql( "charindex(" ); + needle.accept( this ); + appendSql( " collate Latin1_General_100_BIN2," ); + haystack.accept( this ); + append( ")>0" ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/TiDBSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/TiDBSqlAstTranslator.java index 8bce1d70add1..c9920c61d4ba 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/TiDBSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/TiDBSqlAstTranslator.java @@ -201,4 +201,13 @@ public void visitCastTarget(CastTarget castTarget) { super.visitCastTarget( castTarget ); } } + + @Override + protected void renderStringContainsExactlyPredicate(Expression haystack, Expression needle) { + // TiDB can't cope with NUL characters in the position function, so we use a like predicate instead + haystack.accept( this ); + appendSql( " like concat('%',replace(replace(replace(" ); + needle.accept( this ); + appendSql( ",'~','~~'),'?','~?'),'%','~%'),'%') escape '~'" ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index 9e7aa25572ed..a49af9e106df 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -2660,7 +2660,6 @@ private void emulateCycleClauseWithString(SelectClause selectClause) { null, stringType ); - arguments.add( new QueryLiteral<>( "%", stringType ) ); for ( CteColumn cycleColumn : currentCteStatement.getCycleColumns() ) { final int selectionIndex = currentCteStatement.getCteTable() .getCteColumns() @@ -2683,14 +2682,20 @@ private void emulateCycleClauseWithString(SelectClause selectClause) { arguments.add( nullSeparator ); } arguments.add( nullSeparator ); - arguments.add( new QueryLiteral<>( "%", stringType ) ); if ( !supportsRecursiveCycleClause() ) { // Cycle mark appendSql( "case when " ); - visitColumnReference( cyclePathColumnReference ); - appendSql( " like " ); - concat.render( this, arguments, stringType, this ); + renderStringContainsExactlyPredicate( + cyclePathColumnReference, + new SelfRenderingFunctionSqlAstExpression( + "concat", + concat, + arguments, + stringType, + stringType + ) + ); appendSql( " then " ); currentCteStatement.getCycleValue().accept( this ); appendSql( " else " ); @@ -2699,9 +2704,8 @@ private void emulateCycleClauseWithString(SelectClause selectClause) { appendSql( COMMA_SEPARATOR ); } - // Remove the wildcard literals - arguments.remove( arguments.size() - 1 ); - arguments.set( 0, cyclePathColumnReference ); + // Add the previous path + arguments.add( 0, cyclePathColumnReference ); // Cycle path concat.render( this, arguments, stringType, this ); } @@ -2750,6 +2754,18 @@ private void emulateCycleClauseWithString(SelectClause selectClause) { } } + protected void renderStringContainsExactlyPredicate(Expression haystack, Expression needle) { + final AbstractSqmSelfRenderingFunctionDescriptor position = findSelfRenderingFunction( "position", 2 ); + new SelfRenderingFunctionSqlAstExpression( + "position", + position, + List.of( needle, haystack ), + getStringType(), + getStringType() + ).accept( this ); + append( ">0" ); + } + /** * Wraps the given expression so that it produces a string, which should have the same ordering as the original value. * Here are the mappings for various data types: From f5800a0388b9bd1cc946fd20c6f7403f74e8c9e3 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Mon, 15 Jan 2024 10:31:51 +0100 Subject: [PATCH 036/321] HHH-17435 Small fixes to trim() function --- .../hibernate/dialect/AbstractTransactSQLDialect.java | 6 +++--- .../org/hibernate/dialect/function/TrimFunction.java | 6 +++--- .../function/AnsiTrimEmulationFunctionTest.java | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java index 2b6e5252b91b..d2af7fde99fd 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java @@ -183,15 +183,15 @@ public static String replaceLtrimRtrim(TrimSpec specification, boolean isWhitesp case LEADING: return isWhitespace ? "ltrim(?1)" - : "substring(?1,patindex('%[^'+?2+']%',?1),len(?1)-patindex('%[^'+?2+']%',?1)+1)"; + : "substring(?1,patindex('%[^'+?2+']%',?1),len(?1+'x')-1-patindex('%[^'+?2+']%',?1)+1)"; case TRAILING: return isWhitespace ? "rtrim(?1)" - : "substring(?1,1,len(?1)-patindex('%[^'+?2+']%',reverse(?1))+1)"; + : "substring(?1,1,len(?1+'x')-1-patindex('%[^'+?2+']%',reverse(?1))+1)"; default: return isWhitespace ? "ltrim(rtrim(?1))" - : "substring(?1,patindex('%[^'+?2+']%',?1),len(?1)-patindex('%[^'+?2+']%',?1)-patindex('%[^'+?2+']%',reverse(?1))+2)"; + : "substring(?1,patindex('%[^'+?2+']%',?1),len(?1+'x')-1-patindex('%[^'+?2+']%',?1)-patindex('%[^'+?2+']%',reverse(?1))+2)"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/TrimFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/TrimFunction.java index 1f39f70b7260..3ee36151eee7 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/TrimFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/TrimFunction.java @@ -27,13 +27,13 @@ import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.TrimSpecification; -import org.hibernate.type.SqlTypes; import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.spi.TypeConfiguration; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TRIM_SPEC; +import static org.hibernate.type.SqlTypes.isCharacterType; /** * ANSI SQL-standard {@code trim()} function, which has a funny syntax @@ -100,9 +100,9 @@ private static boolean isWhitespace(SqlAstNode trimCharacter) { final JdbcType jdbcType = ( (SqmParameterInterpretation) trimCharacter ).getExpressionType() .getSingleJdbcMapping() .getJdbcType(); - if ( jdbcType.getJdbcTypeCode() != SqlTypes.CHAR ) { + if ( !isCharacterType( jdbcType.getJdbcTypeCode() ) ) { throw new FunctionArgumentException( String.format( - "Expected parameter used as trim character to be Character typed, instead was [%s]", + "Expected parameter used as trim character to be character typed, instead was [%s]", jdbcType.getFriendlyName() ) ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/function/AnsiTrimEmulationFunctionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/function/AnsiTrimEmulationFunctionTest.java index bb895b1de995..38bea21f744c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/function/AnsiTrimEmulationFunctionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/function/AnsiTrimEmulationFunctionTest.java @@ -49,12 +49,12 @@ @ServiceRegistry public class AnsiTrimEmulationFunctionTest { private static final String trimSource = "a.column"; - private static final String LEADING = "substring(?1,patindex('%[^'+?2+']%',?1),len(?1)-patindex('%[^'+?2+']%',?1)+1)"; - private static final String TRAILING = "substring(?1,1,len(?1)-patindex('%[^'+?2+']%',reverse(?1))+1)"; - private static final String BOTH = "substring(?1,patindex('%[^'+?2+']%',?1),len(?1)-patindex('%[^'+?2+']%',?1)-patindex('%[^'+?2+']%',reverse(?1))+2)"; + private static final String LEADING = "substring(?1,patindex('%[^'+?2+']%',?1),len(?1+'x')-1-patindex('%[^'+?2+']%',?1)+1)"; + private static final String TRAILING = "substring(?1,1,len(?1+'x')-1-patindex('%[^'+?2+']%',reverse(?1))+1)"; + private static final String BOTH = "substring(?1,patindex('%[^'+?2+']%',?1),len(?1+'x')-1-patindex('%[^'+?2+']%',?1)-patindex('%[^'+?2+']%',reverse(?1))+2)"; @Test - @RequiresDialect( SQLServerDialect.class ) +// @RequiresDialect( SQLServerDialect.class ) public void testBasicSqlServerProcessing(ServiceRegistryScope scope) { Dialect dialect = new SQLServerDialect(); TrimFunction function = new TrimFunction( dialect, new TypeConfiguration() ); @@ -78,7 +78,7 @@ public void testBasicSqlServerProcessing(ServiceRegistryScope scope) { } @Test - @RequiresDialect( SybaseDialect.class ) +// @RequiresDialect( SybaseDialect.class ) public void testBasicSybaseProcessing(ServiceRegistryScope scope) { Dialect dialect = new SybaseDialect(); TrimFunction function = new TrimFunction( dialect, new TypeConfiguration() ); From 733b555e8682f64596a295cb1b4f1f1cd6652d21 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Thu, 4 Jan 2024 15:58:13 +0100 Subject: [PATCH 037/321] HHH-17619 Add the multitenancy filter in a stateless session --- .../AbstractSharedSessionContract.java | 21 +++++++++++ .../org/hibernate/internal/SessionImpl.java | 22 +----------- .../internal/StatelessSessionImpl.java | 1 + .../orm/test/tenantid/TenantIdTest.java | 36 +++++++++++++++++++ 4 files changed, 59 insertions(+), 21 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index c481be634d84..3a32d26ddf5c 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -29,7 +29,9 @@ import org.hibernate.SessionException; import org.hibernate.Transaction; import org.hibernate.UnknownEntityTypeException; +import org.hibernate.binder.internal.TenantIdBinder; import org.hibernate.cache.spi.CacheTransactionSynchronization; +import org.hibernate.context.spi.CurrentTenantIdentifierResolver; import org.hibernate.engine.internal.SessionEventListenerManagerImpl; import org.hibernate.engine.jdbc.LobCreator; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; @@ -38,6 +40,7 @@ import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.ExceptionConverter; +import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -224,6 +227,24 @@ private static boolean isTransactionCoordinatorShared(SessionCreationOptions opt && ( (SharedSessionCreationOptions) options ).isTransactionCoordinatorShared(); } + protected final void setUpMultitenancy(SessionFactoryImplementor factory, LoadQueryInfluencers loadQueryInfluencers) { + if ( factory.getDefinedFilterNames().contains( TenantIdBinder.FILTER_NAME ) ) { + final Object tenantIdentifier = getTenantIdentifierValue(); + if ( tenantIdentifier == null ) { + throw new HibernateException( "SessionFactory configured for multi-tenancy, but no tenant identifier specified" ); + } + else { + final CurrentTenantIdentifierResolver resolver = factory.getCurrentTenantIdentifierResolver(); + if ( resolver==null || !resolver.isRoot( tenantIdentifier ) ) { + // turn on the filter, unless this is the "root" tenant with access to all partitions + loadQueryInfluencers + .enableFilter( TenantIdBinder.FILTER_NAME ) + .setParameter( TenantIdBinder.PARAMETER_NAME, tenantIdentifier ); + } + } + } + } + private void logInconsistentOptions(SharedSessionCreationOptions sharedOptions) { if ( sharedOptions.shouldAutoJoinTransactions() ) { log.debug( diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 2cb368a85151..b202d1d00e53 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -49,9 +49,7 @@ import org.hibernate.TypeMismatchException; import org.hibernate.UnknownProfileException; import org.hibernate.UnresolvableObjectException; -import org.hibernate.binder.internal.TenantIdBinder; import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.context.spi.CurrentTenantIdentifierResolver; import org.hibernate.engine.internal.StatefulPersistenceContext; import org.hibernate.engine.jdbc.LobCreator; import org.hibernate.engine.jdbc.NonContextualLobCreator; @@ -268,7 +266,7 @@ public SessionImpl(SessionFactoryImpl factory, SessionCreationOptions options) { setHibernateFlushMode( getInitialFlushMode() ); } - setUpMultitenancy( factory ); + setUpMultitenancy( factory, loadQueryInfluencers ); if ( log.isTraceEnabled() ) { log.tracef( "Opened Session [%s] at timestamp: %s", getSessionIdentifier(), currentTimeMillis() ); @@ -283,24 +281,6 @@ private FlushMode getInitialFlushMode() { : ConfigurationHelper.getFlushMode( getSessionProperty( HINT_FLUSH_MODE ), FlushMode.AUTO ); } - private void setUpMultitenancy(SessionFactoryImplementor factory) { - if ( factory.getDefinedFilterNames().contains( TenantIdBinder.FILTER_NAME ) ) { - final Object tenantIdentifier = getTenantIdentifierValue(); - if ( tenantIdentifier == null ) { - throw new HibernateException( "SessionFactory configured for multi-tenancy, but no tenant identifier specified" ); - } - else { - final CurrentTenantIdentifierResolver resolver = factory.getCurrentTenantIdentifierResolver(); - if ( resolver==null || !resolver.isRoot( tenantIdentifier ) ) { - // turn on the filter, unless this is the "root" tenant with access to all partitions - loadQueryInfluencers - .enableFilter( TenantIdBinder.FILTER_NAME ) - .setParameter( TenantIdBinder.PARAMETER_NAME, tenantIdentifier ); - } - } - } - } - protected StatefulPersistenceContext createPersistenceContext() { return new StatefulPersistenceContext( this ); } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java index 45d137630d11..f03b33389f89 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java @@ -78,6 +78,7 @@ public StatelessSessionImpl(SessionFactoryImpl factory, SessionCreationOptions o connectionProvided = options.getConnection() != null; temporaryPersistenceContext = new StatefulPersistenceContext( this ); influencers = new LoadQueryInfluencers( getFactory() ); + setUpMultitenancy( factory, influencers ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/tenantid/TenantIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/tenantid/TenantIdTest.java index d77ed81ca3cb..56dce0aad88f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/tenantid/TenantIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/tenantid/TenantIdTest.java @@ -8,6 +8,7 @@ import org.hibernate.HibernateError; import org.hibernate.PropertyValueException; +import org.hibernate.StatelessSession; import org.hibernate.boot.SessionFactoryBuilder; import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; @@ -20,6 +21,9 @@ import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.Setting; import org.hibernate.binder.internal.TenantIdBinder; +import org.hibernate.query.criteria.HibernateCriteriaBuilder; +import org.hibernate.query.criteria.JpaCriteriaQuery; +import org.hibernate.query.criteria.JpaRoot; import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.jupiter.api.AfterEach; @@ -28,6 +32,7 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; +import static org.assertj.core.api.Assertions.assertThat; import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_DATABASE_ACTION; import static org.hibernate.internal.util.collections.CollectionHelper.toMap; import static org.hibernate.jpa.HibernateHints.HINT_TENANT_ID; @@ -37,6 +42,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import java.util.List; + @SessionFactory @DomainModel(annotatedClasses = { Account.class, Client.class, Record.class }) @ServiceRegistry( @@ -53,6 +60,7 @@ public void cleanup(SessionFactoryScope scope) { scope.inTransaction( session -> { session.createQuery("delete from Account").executeUpdate(); session.createQuery("delete from Client").executeUpdate(); + session.createQuery("delete from Record").executeUpdate(); }); } @@ -217,6 +225,34 @@ public void testEntityManagerHint(SessionFactoryScope scope) { } } + + @Test + public void tenantFilterWithStatelessSession(SessionFactoryScope scope) { + currentTenant = "mine"; + Record myRecord1 = new Record(); + Record myRecord2 = new Record(); + + scope.inTransaction( session -> { + session.persist(myRecord1); + session.persist(myRecord2); + } ); + scope.inStatelessTransaction( session -> { + assertThat( listAllRecordsForTenant( session ) ).hasSize( 2 ); + } ); + + currentTenant = "yours"; + scope.inStatelessTransaction( session -> { + assertThat( listAllRecordsForTenant( session ) ).isEmpty(); + } ); + } + + private static List listAllRecordsForTenant(StatelessSession session) { + HibernateCriteriaBuilder criteriaBuilder = session.getCriteriaBuilder(); + JpaCriteriaQuery criteriaQuery = criteriaBuilder.createQuery( Record.class ); + JpaRoot from = criteriaQuery.from( Record.class ); + return session.createQuery( criteriaQuery ).getResultList(); + } + private static void waitALittle() { try { Thread.sleep( 10 ); From d6f09d6e9d0ac1c299dfb027f13bbc641236a035 Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Thu, 11 Jan 2024 23:36:54 +0100 Subject: [PATCH 038/321] HHH-17635 - register custom user types when contributed through a service provider Signed-off-by: Jan Schatteman --- .../boot/model/process/spi/MetadataBuildingProcess.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java index 8ffe9febb28c..a5353f12c08b 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java @@ -609,6 +609,11 @@ public TypeConfiguration getTypeConfiguration() { public void contributeAttributeConverter(Class> converterClass) { metadataCollector.getConverterRegistry().addAttributeConverter( converterClass ); } + + @Override + public void contributeType(CompositeUserType type) { + options.getCompositeUserTypes().add( type ); + } }; if ( options.getWrapperArrayHandling() == WrapperArrayHandling.LEGACY ) { From 29346e6f19a73a4690fcdfdf6a122668bd0cc4ae Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Mon, 15 Jan 2024 22:47:42 +0100 Subject: [PATCH 039/321] HHH-17635 - Add test for issue Signed-off-by: Jan Schatteman --- .../mapping/basic/MonetaryAmountUserType.java | 2 + .../usertype/ContributedUserTypeTest.java | 69 +++++++++++++++++++ ...ceLoadedCustomUserTypeTypeContributor.java | 23 +++++++ 3 files changed, 94 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/type/contributor/usertype/ServiceLoadedCustomUserTypeTypeContributor.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/MonetaryAmountUserType.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/MonetaryAmountUserType.java index 1b328859d262..c43ee11d44ac 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/MonetaryAmountUserType.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/MonetaryAmountUserType.java @@ -20,6 +20,8 @@ */ public class MonetaryAmountUserType implements CompositeUserType { + public static final MonetaryAmountUserType INSTANCE = new MonetaryAmountUserType(); + @Override public Object getPropertyValue(MonetaryAmount component, int property) throws HibernateException { switch ( property ) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/type/contributor/usertype/ContributedUserTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/contributor/usertype/ContributedUserTypeTest.java index ec5d23a6b6de..a933719981c6 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/type/contributor/usertype/ContributedUserTypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/contributor/usertype/ContributedUserTypeTest.java @@ -7,12 +7,18 @@ package org.hibernate.orm.test.type.contributor.usertype; import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Currency; +import org.hibernate.boot.model.TypeContributor; +import org.hibernate.orm.test.mapping.basic.MonetaryAmount; import org.hibernate.type.CustomType; import org.hibernate.type.Type; import org.hibernate.type.UserComponentType; +import org.hibernate.testing.orm.junit.BootstrapServiceRegistry; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; @@ -29,11 +35,26 @@ annotatedClasses = { ContributedUserTypeTest.StringWrapperTestEntity.class, ContributedUserTypeTest.MyCompositeValueTestEntity.class, + ContributedUserTypeTest.Wallet.class }, typeContributors = { StringWrapperTypeContributor.class, MyCompositeValueTypeContributor.class } ) @SessionFactory +@BootstrapServiceRegistry( + javaServices = { + @BootstrapServiceRegistry.JavaService( role = TypeContributor.class, impl = ServiceLoadedCustomUserTypeTypeContributor.class) + } +) public class ContributedUserTypeTest { + + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createMutationQuery( "delete from Wallet" ).executeUpdate(); + } + ); + } + @Test @JiraKey( "HHH-14408" ) public void test(SessionFactoryScope scope) { @@ -81,6 +102,28 @@ public void testCompositeParameter(SessionFactoryScope scope) { ); } + @Test + @Jira( value = "https://hibernate.atlassian.net/browse/HHH-17635" ) + public void testServiceLoadedCustomUserType(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Wallet wallet = new Wallet(); + wallet.setId( 1L ); + wallet.setMoney( new MonetaryAmount( new BigDecimal( 1000 ), Currency.getInstance("EUR")) ); + session.persist( wallet ); + } + ); + scope.inTransaction( + session -> { + Wallet w = session.createSelectionQuery( "from Wallet", Wallet.class ).getSingleResult(); + MonetaryAmount amount = w.getMoney(); + Assertions.assertNotNull( amount ); + Assertions.assertEquals( 1000, amount.getAmount().intValue() ); + Assertions.assertEquals( "EUR", amount.getCurrency().getCurrencyCode() ); + } + ); + } + @Entity( name = "StringWrapperTestEntity" ) public static class StringWrapperTestEntity implements Serializable { @Id @@ -94,4 +137,30 @@ public static class MyCompositeValueTestEntity implements Serializable { private Integer id; private MyCompositeValue compositeValue; } + + @Entity(name = "Wallet") + public static class Wallet { + + @Id + private Long id; + + private MonetaryAmount money; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public MonetaryAmount getMoney() { + return money; + } + + public void setMoney(MonetaryAmount money) { + this.money = money; + } + } + } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/type/contributor/usertype/ServiceLoadedCustomUserTypeTypeContributor.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/contributor/usertype/ServiceLoadedCustomUserTypeTypeContributor.java new file mode 100644 index 000000000000..6fac70792da7 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/contributor/usertype/ServiceLoadedCustomUserTypeTypeContributor.java @@ -0,0 +1,23 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.type.contributor.usertype; + +import org.hibernate.boot.model.TypeContributions; +import org.hibernate.boot.model.TypeContributor; +import org.hibernate.orm.test.mapping.basic.MonetaryAmountUserType; +import org.hibernate.service.ServiceRegistry; + +/** + * @author Jan Schatteman + */ +public class ServiceLoadedCustomUserTypeTypeContributor implements TypeContributor { + + @Override + public void contribute(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { + typeContributions.contributeType( MonetaryAmountUserType.INSTANCE ); + } +} From 713af21bdfd66b97044425ecae975d6f0b7f3153 Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Thu, 18 Jan 2024 13:42:15 +0000 Subject: [PATCH 040/321] Pre-steps for release : `6.4.2.Final` --- changelog.txt | 52 +++++++++++++++++++++++++++++++++++++++ gradle/version.properties | 2 +- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 1322dac3c6eb..d2f6e9aafd98 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,58 @@ Hibernate 6 Changelog Note: Please refer to JIRA to learn more about each issue. +Changes in 6.4.2.Final (January 18, 2024) +------------------------------------------------------------------------------------------------------------------------ + +https://hibernate.atlassian.net/projects/HHH/versions/32228 + +** Bug + * [HHH-17639] - CTE query cycle attribute still evaluated incorrectly on MSSQL using collation "Latin1_General_CI_AS" + * [HHH-17635] - UnsupportedOperationException when registering CompositeUserType with TypeContributor.contributeType + * [HHH-17632] - AssertionError when updating entity with lazy loading property and bytecode enhancement + * [HHH-17623] - Ordering collection @OrderBy based on association fails + * [HHH-17621] - UnsupportedOperationException when merging an entity with a @Any mapping + * [HHH-17619] - Discriminator-based multitenancy filter is not enabled in a stateless session + * [HHH-17618] - After upgrading to to hibernate 6.3.2 finding relations with non abstract parent class fails. + * [HHH-17615] - @SoftDelete query problem with Joined Inheritance hierarchy structure + * [HHH-17606] - Cannot resolve path of nested generic mapped-superclass joins + * [HHH-17578] - ClassCastException when calling generic typed method on proxy + * [HHH-17574] - Exception on query: Could not convert ‘java.util.Currency’ to ‘java.util.Currency’ using ‘org.hibernate.type.descriptor.java.CurrencyJavaType’ to wrap + * [HHH-17572] - Persist of entity with ToOne association which contains an embeddable with associated collection fails with SemanticException + * [HHH-17566] - addNamedQuery leads to "Named query exists, but did not specify a resultClass" + * [HHH-17560] - ArrayIndexOutOfBoundsException in DirtyHelper.isModified() + * [HHH-17530] - Criteria query with nested dynamic instantiations and sorting is not working + * [HHH-17528] - Explicit selection of an @Embeddable property containing associated collections doesn't work + * [HHH-17515] - DynamicInstantiationResult wrong java type constructor selected + * [HHH-17511] - The @SoftDelete HQL join entity does not generate a delete condition + * [HHH-17507] - Could not convert 'java.time.Year' to 'java.time.Year' using 'org.hibernate.type.descriptor.java.YearJavaType' to wrap + * [HHH-17496] - Inconsistent handling of enum fields + * [HHH-17492] - IN predicate with numeric/decimal parameter types leads to Binding is multi-valued; illegal call to #getBindValue + * [HHH-17490] - NOT IN clause does not work with empty list + * [HHH-17483] - Nested subtypes filtered out in bidirectional OneToMany association load + * [HHH-17481] - OneToMany load empty collection due to discriminator in Polymorphic relationship + * [HHH-17467] - QueryArgumentException when using a proxy as parameter value in a query + * [HHH-17463] - UnsupportedOperationException when using JAKARTA_JDBC_URL + * [HHH-17435] - Trim function not escaping single quotes and not allowing input parameters as trim character + * [HHH-17395] - Refresh with PESSIMISTIC_WRITE ignored for lazy loaded entity + * [HHH-17327] - Adding support for Union All in IncrementGenerator + * [HHH-17307] - Hibernate 6 ListResultsConsumer.Results#addUnique really slow for ElementCollections + * [HHH-17253] - Stackoverflow error on jpamodelgen generation + * [HHH-17106] - Varchar(1) column for Java Enum fails with ClassCastException + * [HHH-16881] - Envers RevisionListener is not created when Hibernate CDI Extensions are enabled + * [HHH-16526] - Query toHqlString() loses function arguments + * [HHH-14821] - EntityKey 'null identifier' should not happen + * [HHH-14358] - Support binding null UUID sql parameter with PostgreSQL + * [HHH-1645] - refresh with LockMode on an unitialized proxy does not work + +** Improvement + * [HHH-17396] - Fix typos in javadoc + * [HHH-17303] - More realistic testcase for HHH-16180 + +** Task + * [HHH-17616] - Move resources to the src/resources folders + + Changes in 6.4.1.Final (December 15, 2023) ------------------------------------------------------------------------------------------------------------------------ diff --git a/gradle/version.properties b/gradle/version.properties index e5993bea2cc0..0ff63bb6fa9f 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -hibernateVersion=6.4.2-SNAPSHOT \ No newline at end of file +hibernateVersion=6.4.2.Final \ No newline at end of file From 03aa53d5c4a8428e98f2a14b6da5ba8b3563331f Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Thu, 18 Jan 2024 13:42:26 +0000 Subject: [PATCH 041/321] Post-steps for release : `6.4.2.Final` --- gradle/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/version.properties b/gradle/version.properties index 0ff63bb6fa9f..ef2f718af356 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -hibernateVersion=6.4.2.Final \ No newline at end of file +hibernateVersion=6.4.3-SNAPSHOT \ No newline at end of file From 8062350cc01019f79362b90014eb6c52910dbadc Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Thu, 11 Jan 2024 16:41:20 +0100 Subject: [PATCH 042/321] HHH-17643 Load `BytecodeProvider` as a java service Also allow `SerializableProxy` deserialization even when no session factory is available. --- .../internal/BytecodeProviderInitiator.java | 44 +++++- .../bytecode/spi/BytecodeProvider.java | 2 + .../org/hibernate/cfg/BytecodeSettings.java | 5 + .../pojo/bytebuddy/SerializableProxy.java | 29 +++- ...rg.hibernate.bytecode.spi.BytecodeProvider | 1 + ...roxySerializationNoSessionFactoryTest.java | 143 ++++++++++++++++++ 6 files changed, 210 insertions(+), 14 deletions(-) create mode 100644 hibernate-core/src/main/resources/META-INF/services/org.hibernate.bytecode.spi.BytecodeProvider create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/serialization/ProxySerializationNoSessionFactoryTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeProviderInitiator.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeProviderInitiator.java index 243852abafec..9bcfd20a9ce4 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeProviderInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeProviderInitiator.java @@ -6,23 +6,40 @@ */ package org.hibernate.bytecode.internal; +import java.util.Collection; +import java.util.Iterator; import java.util.Map; +import java.util.ServiceLoader; import org.hibernate.Internal; import org.hibernate.boot.registry.StandardServiceInitiator; +import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.bytecode.spi.BytecodeProvider; import org.hibernate.internal.CoreMessageLogger; -import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.service.spi.ServiceRegistryImplementor; import org.jboss.logging.Logger; -import static org.hibernate.cfg.BytecodeSettings.BYTECODE_PROVIDER; +import static org.hibernate.internal.util.NullnessUtil.castNonNull; public final class BytecodeProviderInitiator implements StandardServiceInitiator { + /** + * @deprecated Register a {@link BytecodeProvider} through Java {@linkplain java.util.ServiceLoader services}. + */ + @Deprecated( forRemoval = true ) public static final String BYTECODE_PROVIDER_NAME_BYTEBUDDY = "bytebuddy"; + + /** + * @deprecated Register a {@link BytecodeProvider} through Java {@linkplain java.util.ServiceLoader services}. + */ + @Deprecated( forRemoval = true ) public static final String BYTECODE_PROVIDER_NAME_NONE = "none"; + + /** + * @deprecated Deprecated with no replacement + */ + @Deprecated( forRemoval = true ) public static final String BYTECODE_PROVIDER_NAME_DEFAULT = BYTECODE_PROVIDER_NAME_BYTEBUDDY; /** @@ -32,8 +49,9 @@ public final class BytecodeProviderInitiator implements StandardServiceInitiator @Override public BytecodeProvider initiateService(Map configurationValues, ServiceRegistryImplementor registry) { - String provider = ConfigurationHelper.getString( BYTECODE_PROVIDER, configurationValues, BYTECODE_PROVIDER_NAME_DEFAULT ); - return buildBytecodeProvider( provider ); + final ClassLoaderService classLoaderService = castNonNull( registry.getService( ClassLoaderService.class ) ); + final Collection bytecodeProviders = classLoaderService.loadJavaServices( BytecodeProvider.class ); + return getBytecodeProvider( bytecodeProviders ); } @Override @@ -43,12 +61,26 @@ public Class getServiceInitiated() { @Internal public static BytecodeProvider buildDefaultBytecodeProvider() { - return buildBytecodeProvider( BYTECODE_PROVIDER_NAME_BYTEBUDDY ); + return getBytecodeProvider( ServiceLoader.load( BytecodeProvider.class ) ); } @Internal - public static BytecodeProvider buildBytecodeProvider(String providerName) { + public static BytecodeProvider getBytecodeProvider(Iterable bytecodeProviders) { + final Iterator iterator = bytecodeProviders.iterator(); + if ( !iterator.hasNext() ) { + // If no BytecodeProvider service is available, default to the "no-op" enhancer + return new org.hibernate.bytecode.internal.none.BytecodeProviderImpl(); + } + + final BytecodeProvider provider = iterator.next(); + if ( iterator.hasNext() ) { + throw new IllegalStateException( "Found multiple BytecodeProvider service registrations, cannot determine which one to use" ); + } + return provider; + } + @Internal + public static BytecodeProvider buildBytecodeProvider(String providerName) { CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, BytecodeProviderInitiator.class.getName() ); LOG.bytecodeProvider( providerName ); diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/spi/BytecodeProvider.java b/hibernate-core/src/main/java/org/hibernate/bytecode/spi/BytecodeProvider.java index 2b9c33653559..077c9e5869d6 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/spi/BytecodeProvider.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/spi/BytecodeProvider.java @@ -11,6 +11,7 @@ import org.hibernate.bytecode.enhance.spi.EnhancementContext; import org.hibernate.bytecode.enhance.spi.Enhancer; import org.hibernate.property.access.spi.PropertyAccess; +import org.hibernate.service.JavaServiceLoadable; import org.hibernate.service.Service; import org.checkerframework.checker.nullness.qual.Nullable; @@ -25,6 +26,7 @@ * * @author Steve Ebersole */ +@JavaServiceLoadable public interface BytecodeProvider extends Service { /** * Retrieve the specific factory for this provider capable of diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/BytecodeSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/BytecodeSettings.java index 1f1b4efdcb2f..df5d7801e2da 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/BytecodeSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/BytecodeSettings.java @@ -20,7 +20,12 @@ public interface BytecodeSettings { * At present only bytebuddy is supported, bytebuddy being the default since version 5.3. * * @settingDefault {@code "bytebuddy"} + * @deprecated Will be removed, Hibernate ORM will use the BytecodeProvider implementation it finds on the + * classpath loading it via the standard ServiceLoader mechanism. Currently, there is only a single + * implementation which is included in Hibernate ORM, so it's not possible to override this. + * See HHH-17643 */ + @Deprecated( forRemoval = true ) String BYTECODE_PROVIDER = "hibernate.bytecode.provider"; /** diff --git a/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/SerializableProxy.java b/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/SerializableProxy.java index ad0113ac929c..f34d8eb631c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/SerializableProxy.java +++ b/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/SerializableProxy.java @@ -17,6 +17,8 @@ import org.hibernate.proxy.HibernateProxy; import org.hibernate.type.CompositeType; +import static org.hibernate.bytecode.internal.BytecodeProviderInitiator.buildDefaultBytecodeProvider; + public final class SerializableProxy extends AbstractSerializableProxy { private final Class persistentClass; private final Class[] interfaces; @@ -30,6 +32,8 @@ public final class SerializableProxy extends AbstractSerializableProxy { private final CompositeType componentIdType; + private static volatile BytecodeProviderImpl fallbackBytecodeProvider; + public SerializableProxy( String entityName, Class persistentClass, @@ -120,17 +124,27 @@ private Object readResolve() { private static SessionFactoryImplementor retrieveMatchingSessionFactory(final String sessionFactoryUuid, final String sessionFactoryName) { Objects.requireNonNull( sessionFactoryUuid ); - final SessionFactoryImplementor sessionFactory = SessionFactoryRegistry.INSTANCE.findSessionFactory( sessionFactoryUuid, sessionFactoryName ); - if ( sessionFactory != null ) { - return sessionFactory; + return SessionFactoryRegistry.INSTANCE.findSessionFactory( sessionFactoryUuid, sessionFactoryName ); + } + + private static BytecodeProviderImpl retrieveByteBuddyBytecodeProvider(final SessionFactoryImplementor sessionFactory) { + if ( sessionFactory == null ) { + // When the session factory is not available fallback to local bytecode provider + return getFallbackBytecodeProvider(); } - else { - throw new IllegalStateException( "Could not identify any active SessionFactory having UUID " + sessionFactoryUuid ); + + return castBytecodeProvider( sessionFactory.getServiceRegistry().getService( BytecodeProvider.class ) ); + } + + private static BytecodeProviderImpl getFallbackBytecodeProvider() { + BytecodeProviderImpl provider = fallbackBytecodeProvider; + if ( provider == null ) { + provider = fallbackBytecodeProvider = castBytecodeProvider( buildDefaultBytecodeProvider() ); } + return provider; } - private static BytecodeProviderImpl retrieveByteBuddyBytecodeProvider(final SessionFactoryImplementor sessionFactory) { - final BytecodeProvider bytecodeProvider = sessionFactory.getServiceRegistry().getService( BytecodeProvider.class ); + private static BytecodeProviderImpl castBytecodeProvider(BytecodeProvider bytecodeProvider) { if ( bytecodeProvider instanceof BytecodeProviderImpl ) { return (BytecodeProviderImpl) bytecodeProvider; } @@ -138,5 +152,4 @@ private static BytecodeProviderImpl retrieveByteBuddyBytecodeProvider(final Sess throw new IllegalStateException( "Unable to deserialize a SerializableProxy proxy: the bytecode provider is not ByteBuddy." ); } } - } diff --git a/hibernate-core/src/main/resources/META-INF/services/org.hibernate.bytecode.spi.BytecodeProvider b/hibernate-core/src/main/resources/META-INF/services/org.hibernate.bytecode.spi.BytecodeProvider new file mode 100644 index 000000000000..a2e4f6d2a57e --- /dev/null +++ b/hibernate-core/src/main/resources/META-INF/services/org.hibernate.bytecode.spi.BytecodeProvider @@ -0,0 +1 @@ +org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/serialization/ProxySerializationNoSessionFactoryTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/serialization/ProxySerializationNoSessionFactoryTest.java new file mode 100644 index 000000000000..721884c88d19 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/serialization/ProxySerializationNoSessionFactoryTest.java @@ -0,0 +1,143 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.serialization; + +import java.io.Serializable; + +import org.hibernate.Hibernate; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; +import org.hibernate.internal.SessionFactoryRegistry; +import org.hibernate.internal.util.SerializationHelper; +import org.hibernate.proxy.HibernateProxy; + +import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.hibernate.testing.util.ServiceRegistryUtil; +import org.junit.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * @author Marco Belladelli + */ +public class ProxySerializationNoSessionFactoryTest extends BaseUnitTestCase { + @Test + public void testUninitializedProxy() { + executeTest( false ); + } + + @Test + public void testInitializedProxy() { + executeTest( true ); + } + + private void executeTest(boolean initializeProxy) { + final Configuration cfg = new Configuration() + .setProperty( AvailableSettings.HBM2DDL_AUTO, "create-drop" ) + .addAnnotatedClass( SimpleEntity.class ) + .addAnnotatedClass( ChildEntity.class ); + ServiceRegistryUtil.applySettings( cfg.getStandardServiceRegistryBuilder() ); + final SimpleEntity parent; + try (final SessionFactory factory = cfg.buildSessionFactory()) { + doInHibernate( () -> factory, session -> { + final SimpleEntity entity = new SimpleEntity(); + entity.setId( 1L ); + entity.setName( "TheParent" ); + session.persist( entity ); + + final ChildEntity child = new ChildEntity(); + child.setId( 1L ); + child.setParent( entity ); + session.persist( child ); + } ); + + parent = doInHibernate( () -> factory, session -> { + final ChildEntity childEntity = session.find( ChildEntity.class, 1L ); + final SimpleEntity entity = childEntity.getParent(); + if ( initializeProxy ) { + assertEquals( "TheParent",entity.getName() ); + } + return entity; + } ); + } + + // The session factory is not available anymore + assertFalse( SessionFactoryRegistry.INSTANCE.hasRegistrations() ); + + assertTrue( parent instanceof HibernateProxy ); + assertEquals( initializeProxy, Hibernate.isInitialized( parent ) ); + + // Serialization and deserialization should still work + final SimpleEntity clone = (SimpleEntity) SerializationHelper.clone( parent ); + assertNotNull( clone ); + assertEquals( parent.getId(), clone.getId() ); + if ( initializeProxy ) { + assertEquals( parent.getName(), clone.getName() ); + } + } + + @Entity( name = "SimpleEntity" ) + static class SimpleEntity implements Serializable { + @Id + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(final Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + } + + @Entity( name = "ChildEntity" ) + static class ChildEntity { + @Id + private Long id; + + @ManyToOne( fetch = FetchType.LAZY ) + @JoinColumn + private SimpleEntity parent; + + public Long getId() { + return id; + } + + public void setId(final Long id) { + this.id = id; + } + + public SimpleEntity getParent() { + return parent; + } + + public void setParent(SimpleEntity parent) { + this.parent = parent; + } + } +} From 240514986d3cdd1f6790f71987578e1d95167b65 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Thu, 18 Jan 2024 15:33:31 +0100 Subject: [PATCH 043/321] HHH-17643 Remove unused internal BytecodeProviderInitiator method --- .../internal/BytecodeProviderInitiator.java | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeProviderInitiator.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeProviderInitiator.java index 9bcfd20a9ce4..4e2487540c57 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeProviderInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeProviderInitiator.java @@ -15,11 +15,8 @@ import org.hibernate.boot.registry.StandardServiceInitiator; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.bytecode.spi.BytecodeProvider; -import org.hibernate.internal.CoreMessageLogger; import org.hibernate.service.spi.ServiceRegistryImplementor; -import org.jboss.logging.Logger; - import static org.hibernate.internal.util.NullnessUtil.castNonNull; public final class BytecodeProviderInitiator implements StandardServiceInitiator { @@ -78,30 +75,4 @@ public static BytecodeProvider getBytecodeProvider(Iterable by } return provider; } - - @Internal - public static BytecodeProvider buildBytecodeProvider(String providerName) { - CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, BytecodeProviderInitiator.class.getName() ); - LOG.bytecodeProvider( providerName ); - - if ( BYTECODE_PROVIDER_NAME_NONE.equals( providerName ) ) { - return new org.hibernate.bytecode.internal.none.BytecodeProviderImpl(); - } - if ( BYTECODE_PROVIDER_NAME_BYTEBUDDY.equals( providerName ) ) { - return new org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl(); - } - - // There is no need to support plugging in a custom BytecodeProvider via FQCN - // as it's possible to plug a custom BytecodeProviderInitiator into the bootstrap. - // - // This also allows integrators to inject a BytecodeProvider instance which has some - // state: particularly useful to inject proxy definitions which have been prepared in - // advance. - // See also https://hibernate.atlassian.net/browse/HHH-13804 and how this was solved in - // Quarkus. - - LOG.unknownBytecodeProvider( providerName, BYTECODE_PROVIDER_NAME_DEFAULT ); - return new org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl(); - } - } From 4ba772b72d18cff7a42297b7ce1267c42ff1b2da Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Fri, 19 Jan 2024 11:57:59 +0100 Subject: [PATCH 044/321] HHH-17653 Add test for issue --- ...dableFieldsShouldThrowAnExceptionTest.java | 70 +++++++++++++++++++ ...dToIdFieldsShouldThrowAnExceptionTest.java | 60 ++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/idgen/GeneratorNotAppliedToIdEmbeddableFieldsShouldThrowAnExceptionTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/idgen/GeneratorNotAppliedToIdFieldsShouldThrowAnExceptionTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/GeneratorNotAppliedToIdEmbeddableFieldsShouldThrowAnExceptionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/GeneratorNotAppliedToIdEmbeddableFieldsShouldThrowAnExceptionTest.java new file mode 100644 index 000000000000..c71a9ce05867 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/GeneratorNotAppliedToIdEmbeddableFieldsShouldThrowAnExceptionTest.java @@ -0,0 +1,70 @@ +package org.hibernate.orm.test.idgen; + +import org.hibernate.AnnotationException; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.service.ServiceRegistry; + +import org.hibernate.testing.orm.junit.BaseUnitTest; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; +import org.hibernate.testing.util.ServiceRegistryUtil; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Embeddable; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@BaseUnitTest +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsIdentityColumns.class) +@Jira("HHH-17653") +public class GeneratorNotAppliedToIdEmbeddableFieldsShouldThrowAnExceptionTest { + protected ServiceRegistry serviceRegistry; + protected MetadataImplementor metadata; + + @AfterEach + public void tearDown() { + StandardServiceRegistryBuilder.destroy( serviceRegistry ); + } + + @Test + public void testThatAnAnnotationExceptionIsThrown() { + Exception exception = assertThrows( AnnotationException.class, () -> { + serviceRegistry = ServiceRegistryUtil.serviceRegistry(); + metadata = (MetadataImplementor) new MetadataSources( serviceRegistry ) + .addAnnotatedClass( TestEntity.class ) + .buildMetadata(); + } ); + assertThat( exception.getMessage() ).contains( "Property 'serialValue'" ); + } + + @Entity(name = "TestEntity") + @Table(name = "TEST_ENITY") + public static class TestEntity { + + @Id + private String id; + + @Embedded + private TestEmbeddable testEmbeddable; + + } + + @Embeddable + public static class TestEmbeddable { + + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer serialValue; + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/GeneratorNotAppliedToIdFieldsShouldThrowAnExceptionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/GeneratorNotAppliedToIdFieldsShouldThrowAnExceptionTest.java new file mode 100644 index 000000000000..00d75e53baec --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/GeneratorNotAppliedToIdFieldsShouldThrowAnExceptionTest.java @@ -0,0 +1,60 @@ +package org.hibernate.orm.test.idgen; + +import org.hibernate.AnnotationException; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.service.ServiceRegistry; + +import org.hibernate.testing.orm.junit.BaseUnitTest; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; +import org.hibernate.testing.util.ServiceRegistryUtil; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@BaseUnitTest +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsIdentityColumns.class) +@Jira("HHH-17653") +public class GeneratorNotAppliedToIdFieldsShouldThrowAnExceptionTest { + protected ServiceRegistry serviceRegistry; + protected MetadataImplementor metadata; + + @AfterEach + public void tearDown() { + StandardServiceRegistryBuilder.destroy( serviceRegistry ); + } + + @Test + public void testThatAnAnnotationExceptionIsThrown() { + Exception exception = assertThrows( AnnotationException.class, () -> { + serviceRegistry = ServiceRegistryUtil.serviceRegistry(); + metadata = (MetadataImplementor) new MetadataSources( serviceRegistry ) + .addAnnotatedClass( TestEntity.class ) + .buildMetadata(); + } ); + assertThat( exception.getMessage() ).contains( "Property '" + TestEntity.class.getName() + ".name'" ); + } + + @Entity(name = "TestEntity") + @Table(name = "TEST_ENITY") + public static class TestEntity { + + @Id + private String id; + + @GeneratedValue(strategy = GenerationType.IDENTITY) + private String name; + + } +} From 673f03304f78aec83912000198395349452b2b5c Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Fri, 19 Jan 2024 11:59:48 +0100 Subject: [PATCH 045/321] HHH-17653 - Error in generating schema when @Generator annotation is applied to a non id embeddable property --- .../boot/model/internal/EmbeddableBinder.java | 10 +++++++- .../boot/model/internal/EntityBinder.java | 12 +++++++++- migration-guide.adoc | 24 +++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java index 8781b94be99a..674adad8f6a9 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java @@ -388,7 +388,15 @@ static Component fillEmbeddable( final XProperty property = propertyAnnotatedElement.getProperty(); if ( property.isAnnotationPresent( GeneratedValue.class ) ) { - processGeneratedId( context, component, property ); + if ( isIdClass || subholder.isOrWithinEmbeddedId() ) { + processGeneratedId( context, component, property ); + } + else { + throw new AnnotationException( + "Property '" + property.getName() + "' of '" + + getPath( propertyHolder, inferredData ) + + "' is annotated '@GeneratedValue' but is not part of an identifier" ); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java index 527a30c39c94..d6e913d48c61 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java @@ -67,6 +67,7 @@ import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.annotations.common.reflection.XAnnotatedElement; import org.hibernate.annotations.common.reflection.XClass; +import org.hibernate.annotations.common.reflection.XProperty; import org.hibernate.binder.TypeBinder; import org.hibernate.boot.model.IdentifierGeneratorDefinition; import org.hibernate.boot.model.NamedEntityGraphDefinition; @@ -116,6 +117,7 @@ import jakarta.persistence.DiscriminatorColumn; import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; import jakarta.persistence.IdClass; import jakarta.persistence.Inheritance; import jakarta.persistence.InheritanceType; @@ -1015,14 +1017,22 @@ private void processIdPropertiesIfNotAlready( for ( PropertyData propertyAnnotatedElement : elementsToProcess.getElements() ) { final String propertyName = propertyAnnotatedElement.getPropertyName(); if ( !idPropertiesIfIdClass.contains( propertyName ) ) { + final XProperty property = propertyAnnotatedElement.getProperty(); + boolean hasIdAnnotation = hasIdAnnotation( property ); if ( !idPropertiesIfIdClass.isEmpty() && !isIgnoreIdAnnotations() - && hasIdAnnotation( propertyAnnotatedElement.getProperty() ) ) { + && hasIdAnnotation ) { missingEntityProperties.add( propertyName ); } else { boolean subclassAndSingleTableStrategy = inheritanceState.getType() == InheritanceType.SINGLE_TABLE && inheritanceState.hasParents(); + if ( !hasIdAnnotation && property.isAnnotationPresent( GeneratedValue.class ) ) { + throw new AnnotationException( + "Property '" + + BinderHelper.getPath( propertyHolder, propertyAnnotatedElement ) + + "' is annotated @GeneratedValue but is not part of an identifier" ); + } processElementAnnotations( propertyHolder, subclassAndSingleTableStrategy diff --git a/migration-guide.adoc b/migration-guide.adoc index 6d90b7ca69b7..d91cbdea9e67 100644 --- a/migration-guide.adoc +++ b/migration-guide.adoc @@ -76,3 +76,27 @@ Finally, the `SelfRenderingFunctionSqlAstExpression.getRenderer()` method was de == Supported Dialects Please also do have a look at the link:{versionDocBase}/dialect/dialect.html[Supported Dialects] document to check for potential upgrades to the minimum version of the databases underpinning the dialects that Hibernate currently supports. + +[[GeneratedValues]] +== Generated Values + +Previous versions of Hibernate silently ignored annotations `@GeneratedValue` applied to non identifier fields. + +E.g. + +``` +@Entity +class Entity{ + + @Id + Integer id, + + + + @GeneratedValue // Ignored in Hibernate versions < 6.4, since 6.4 an AnnotationException is thrown + String name; +} + +``` + +From 6.4 this mapping is not allowed anymore and an `AnnotationException` will be raised. From 70c8ea03bb945cd93ad731dbc58878acba42704f Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 5 Dec 2023 12:50:37 +0100 Subject: [PATCH 046/321] HHH-17461 Include soft-delete column in duplication check --- .../hibernate/mapping/PersistentClass.java | 3 + .../SoftDeleteMappedColumnTest.java | 120 ++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/softdelete/SoftDeleteMappedColumnTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java b/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java index 0008092a4bab..331c18c38575 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java @@ -932,6 +932,9 @@ protected void checkColumnDuplication() { if ( isDiscriminatorInsertable() && getDiscriminator() != null ) { getDiscriminator().checkColumnDuplication( cols, owner ); } + if ( getRootClass().getSoftDeleteColumn() != null ) { + getRootClass().getSoftDeleteColumn().getValue().checkColumnDuplication( cols, owner ); + } checkPropertyColumnDuplication( cols, getNonDuplicatedProperties(), owner ); for ( Join join : getJoins() ) { cols.clear(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/softdelete/SoftDeleteMappedColumnTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/softdelete/SoftDeleteMappedColumnTest.java new file mode 100644 index 000000000000..d4c1c08cff72 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/softdelete/SoftDeleteMappedColumnTest.java @@ -0,0 +1,120 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.softdelete; + +import org.hibernate.MappingException; +import org.hibernate.SessionFactory; +import org.hibernate.annotations.SoftDelete; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; +import org.hibernate.tool.schema.Action; + +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.util.ServiceRegistryUtil; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * @author Marco Belladelli + */ +@Jira( "https://hibernate.atlassian.net/browse/HHH-17461" ) +public class SoftDeleteMappedColumnTest { + @Test + public void testValid() { + try (final SessionFactory sf = buildSessionFactory( ValidEntity.class )) { + sf.inTransaction( session -> { + final ValidEntity validEntity = new ValidEntity( 1L, "valid1" ); + session.persist( validEntity ); + session.flush(); + assertThat( validEntity.isDeleted() ).isFalse(); + session.remove( validEntity ); + } ); + sf.inSession( session -> assertThat( session.find( ValidEntity.class, 1L ) ).isNull() ); + } + } + + @Test + public void testInvalid() { + try (final SessionFactory sf = buildSessionFactory( InvalidEntity.class )) { + sf.inTransaction( session -> { + final InvalidEntity entity = new InvalidEntity( 2L, "invalid2" ); + session.persist( entity ); + } ); + fail( "Duplicate soft-delete column should fail" ); + } + catch (Exception e) { + assertThat( e ).isInstanceOf( MappingException.class ); + assertThat( e.getMessage() ).contains( "Column 'is_deleted' is duplicated" ); + } + } + + private SessionFactory buildSessionFactory(Class entityClass) { + final Configuration cfg = new Configuration() + .setProperty( AvailableSettings.JAKARTA_HBM2DDL_DATABASE_ACTION, Action.ACTION_CREATE_THEN_DROP ) + .addAnnotatedClass( entityClass ); + ServiceRegistryUtil.applySettings( cfg.getStandardServiceRegistryBuilder() ); + return cfg.buildSessionFactory(); + } + + @Entity( name = "ValidEntity" ) + @SoftDelete( columnName = "is_deleted" ) + public static class ValidEntity { + @Id + private Long id; + + private String name; + + @Column( name = "is_deleted", insertable = false, updatable = false ) + private boolean deleted; + + public ValidEntity() { + } + + public ValidEntity(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public boolean isDeleted() { + return deleted; + } + } + + @Entity( name = "InvalidEntity" ) + @SoftDelete( columnName = "is_deleted" ) + public static class InvalidEntity { + @Id + private Long id; + + private String name; + + @Column( name = "is_deleted" ) + private boolean deleted; + + public InvalidEntity() { + } + + public InvalidEntity(Long id, String name) { + this.id = id; + this.name = name; + } + } +} From d25b5532a9bed94e2014ce977731d67f7202db62 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Mon, 8 Jan 2024 12:00:48 +0100 Subject: [PATCH 047/321] HHH-17605 Fix native query selecting multiple of the same entity result --- .../internal/ResultSetMappingProcessor.java | 2 +- .../NativeQueryMultipleEntityResultTest.java | 83 +++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/NativeQueryMultipleEntityResultTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java index c4100379067f..c051eb564647 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java @@ -298,7 +298,7 @@ private DynamicResultBuilderEntityStandard createSuffixedResultBuilder( rootReturn.getTableAlias(), suffix, rootReturn.getLockMode(), - new NavigablePath( rootReturn.getEntityMapping().getEntityName() ) + new NavigablePath( rootReturn.getEntityMapping().getEntityName(), rootReturn.getTableAlias() ) ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/NativeQueryMultipleEntityResultTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/NativeQueryMultipleEntityResultTest.java new file mode 100644 index 000000000000..90e91dc2d4ec --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/NativeQueryMultipleEntityResultTest.java @@ -0,0 +1,83 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.sql; + +import org.hibernate.testing.orm.domain.gambit.BasicEntity; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Tuple; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Marco Belladelli + */ +@DomainModel( annotatedClasses = BasicEntity.class ) +@SessionFactory +@Jira( "https://hibernate.atlassian.net/browse/HHH-17605" ) +public class NativeQueryMultipleEntityResultTest { + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.persist( new BasicEntity( 1, "entity_1" ) ); + session.persist( new BasicEntity( 2, "entity_2" ) ); + } ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createMutationQuery( "delete from BasicEntity" ).executeUpdate() ); + } + + @Test + public void testEntityResult(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final BasicEntity result = session.createNativeQuery( + "select be.id, be.data from BasicEntity be where be.id = 1", + BasicEntity.class + ).getSingleResult(); + assertThat( result.getId() ).isEqualTo( 1 ); + assertThat( result.getData() ).isEqualTo( "entity_1" ); + } ); + } + + @Test + public void testSingleAlias(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final BasicEntity result = session.createNativeQuery( + "SELECT {be1.*} FROM BasicEntity be1 WHERE be1.id = 1", + BasicEntity.class, + "be1" + ).getSingleResult(); + assertThat( result.getId() ).isEqualTo( 1 ); + assertThat( result.getData() ).isEqualTo( "entity_1" ); + } ); + } + + @Test + public void testMultipleAliases(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final Tuple result = session.createNativeQuery( + "SELECT {be1.*}, {be2.*} FROM BasicEntity be1, BasicEntity be2 WHERE be1.id = 1 and be2.id = 2", + Tuple.class + ) + .addEntity( "be1", BasicEntity.class ) + .addEntity( "be2", BasicEntity.class ) + .getSingleResult(); + assertThat( result.get( 0, BasicEntity.class ).getId() ).isEqualTo( 1 ); + assertThat( result.get( 0, BasicEntity.class ).getData() ).isEqualTo( "entity_1" ); + assertThat( result.get( 1, BasicEntity.class ).getId() ).isEqualTo( 2 ); + assertThat( result.get( 1, BasicEntity.class ).getData() ).isEqualTo( "entity_2" ); + } ); + } +} From bb047c4fad414aa6d64c437445fd252f404d5092 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Fri, 15 Dec 2023 10:50:35 +0100 Subject: [PATCH 048/321] HHH-17526 Add test for issue --- .../batch/ManyToOneBatchLoadErrorTest.java | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/batch/ManyToOneBatchLoadErrorTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/batch/ManyToOneBatchLoadErrorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/batch/ManyToOneBatchLoadErrorTest.java new file mode 100644 index 000000000000..2501654c154b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/batch/ManyToOneBatchLoadErrorTest.java @@ -0,0 +1,142 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.batch; + +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.PersistenceException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +/** + * @author Marco Belladelli + */ +@DomainModel( annotatedClasses = { + ManyToOneBatchLoadErrorTest.Parent.class, + ManyToOneBatchLoadErrorTest.Child.class +} ) +@SessionFactory +public class ManyToOneBatchLoadErrorTest { + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final Child child1 = new Child( "TYPE_ONE" ); + session.persist( child1 ); + session.persist( new Parent( 1L, child1 ) ); + final Child child2 = new Child( "TYPE_TWO" ); + session.persist( child2 ); + session.persist( new Parent( 2L, child2 ) ); + } ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.createMutationQuery( "delete from Parent" ).executeUpdate(); + session.createMutationQuery( "delete from Child" ).executeUpdate(); + } ); + } + + @Test + public void testRuntimeException(SessionFactoryScope scope) { + scope.inSession( session -> { + try { + session.find( Parent.class, 1L ); + fail( "Expected RuntimeException to be thrown in AttributeConverter" ); + } + catch (Exception e) { + assertThat( e ) + .isInstanceOf( PersistenceException.class ) + .hasMessageContaining( "Error attempting to apply AttributeConverter" ); + } + } ); + } + + @Test + public void testWorking(SessionFactoryScope scope) { + scope.inSession( session -> { + final Parent parent = session.find( Parent.class, 2L ); + assertThat( parent.getChild() ).isNotNull(); + assertThat( parent.getChild().getType() ).isEqualTo( "TYPE_TWO" ); + } ); + } + + @Entity( name = "Parent" ) + public static class Parent { + @Id + private Long id; + + @ManyToOne + @Fetch( value = FetchMode.SELECT ) + private Child child; + + public Parent() { + } + + public Parent(Long id, Child child) { + this.id = id; + this.child = child; + } + + public Child getChild() { + return child; + } + } + + @Entity( name = "Child" ) + @BatchSize( size = 5 ) + public static class Child { + @Id + @GeneratedValue + private Long id; + + @Convert( converter = ChildTypeConverter.class ) + private String type; + + public Child() { + } + + public Child(String type) { + this.type = type; + } + + public String getType() { + return type; + } + } + + public static class ChildTypeConverter implements AttributeConverter { + @Override + public String convertToDatabaseColumn(String attribute) { + return attribute; + } + + @Override + public String convertToEntityAttribute(String dbData) { + if ( dbData.equals( "TYPE_ONE" ) ) { + throw new RuntimeException(); + } + return dbData; + } + } +} From ba5101d40b2df39d146ee334c3d42be58d288347 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Fri, 15 Dec 2023 10:50:42 +0100 Subject: [PATCH 049/321] HHH-17526 Throw correct exception when reading list results --- .../sql/results/spi/ListResultsConsumer.java | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java index 0a6988164789..79718e5d5f2c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java @@ -152,10 +152,9 @@ public List consume( final TypeConfiguration typeConfiguration = session.getTypeConfiguration(); final QueryOptions queryOptions = rowProcessingState.getQueryOptions(); RuntimeException ex = null; + persistenceContext.beforeLoad(); + persistenceContext.getLoadContexts().register( jdbcValuesSourceProcessingState ); try { - persistenceContext.beforeLoad(); - persistenceContext.getLoadContexts().register( jdbcValuesSourceProcessingState ); - final JavaType domainResultJavaType = resolveDomainResultJavaType( rowReader.getDomainResultResultJavaType(), rowReader.getResultJavaTypes(), @@ -207,13 +206,8 @@ else if ( uniqueSemantic == UniqueSemantic.ASSERT ) { } } - try { - rowReader.finishUp( jdbcValuesSourceProcessingState ); - jdbcValuesSourceProcessingState.finishUp( readRows > 1 ); - } - finally { - persistenceContext.getLoadContexts().deregister( jdbcValuesSourceProcessingState ); - } + rowReader.finishUp( jdbcValuesSourceProcessingState ); + jdbcValuesSourceProcessingState.finishUp( readRows > 1 ); //noinspection unchecked final ResultListTransformer resultListTransformer = @@ -231,6 +225,7 @@ else if ( uniqueSemantic == UniqueSemantic.ASSERT ) { try { jdbcValues.finishUp( session ); persistenceContext.afterLoad(); + persistenceContext.getLoadContexts().deregister( jdbcValuesSourceProcessingState ); persistenceContext.initializeNonLazyCollections(); } catch (RuntimeException e) { From db47b51a308df9f40cbdf06c137ab6450e33b801 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 10 Jan 2024 16:01:41 +0100 Subject: [PATCH 050/321] HHH-17634 Add test for issue --- ...ociationsAndGeneratedValuesMerge2Test.java | 117 ++++++++++++++++++ .../orm/test/merge/SimpleMergeTest.java | 62 ++++++++++ 2 files changed, 179 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/merge/CompositeIdWithAssociationsAndGeneratedValuesMerge2Test.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/merge/SimpleMergeTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/merge/CompositeIdWithAssociationsAndGeneratedValuesMerge2Test.java b/hibernate-core/src/test/java/org/hibernate/orm/test/merge/CompositeIdWithAssociationsAndGeneratedValuesMerge2Test.java new file mode 100644 index 000000000000..9a2edbc5131c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/merge/CompositeIdWithAssociationsAndGeneratedValuesMerge2Test.java @@ -0,0 +1,117 @@ +package org.hibernate.orm.test.merge; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@DomainModel( + annotatedClasses = { + CompositeIdWithAssociationsAndGeneratedValuesMerge2Test.Middle.class, + CompositeIdWithAssociationsAndGeneratedValuesMerge2Test.Bottom.class + } +) +@SessionFactory +@JiraKey( "HHH-17634" ) +public class CompositeIdWithAssociationsAndGeneratedValuesMerge2Test { + + @Test + public void testMerge(SessionFactoryScope scope) { + + scope.inTransaction( + session -> { + Middle m1 = new Middle( "Middle" ); + Bottom bottom = new Bottom( m1, 0, "Bottom" ); + Middle merge = session.merge( m1 ); + assertThat( merge.getId() ).isNotNull(); + assertThat( m1.getId() ).isNull(); + } + ); + } + + @Entity(name = "Middle") + @Table(name = "middle_table") + public static class Middle { + @Id + @GeneratedValue + private Long id; + + private String name; + + @OneToMany(mappedBy = "middle", cascade = { CascadeType.MERGE, CascadeType.PERSIST }) + private List bottoms; + + public Middle() { + } + + public Middle(String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + + public String getName() { + return name; + } + + + public List getBottoms() { + return bottoms; + } + + + public void addBottom(Bottom bottom) { + if ( bottoms == null ) { + bottoms = new ArrayList<>(); + } + bottoms.add( bottom ); + } + } + + @Entity(name = "Bottom") + @Table(name = "bottom_table") + public static class Bottom { + @Id + @ManyToOne(optional = false, cascade = CascadeType.MERGE) + @JoinColumn(name = "middle_id", nullable = false) + private Middle middle; + + @Id + @Column(name = "type_column") + private Integer type; + + private String note; + + public Bottom() { + } + + public Bottom(Middle middle, Integer type,String note) { + this.middle = middle; + this.middle.addBottom( this ); + this.type = type; + this.note = note; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/merge/SimpleMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/merge/SimpleMergeTest.java new file mode 100644 index 000000000000..cc0b3213236f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/merge/SimpleMergeTest.java @@ -0,0 +1,62 @@ +package org.hibernate.orm.test.merge; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; + +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +import static org.assertj.core.api.Assertions.assertThat; + +@DomainModel( + annotatedClasses = { + SimpleMergeTest.MyEntity.class + } +) +@SessionFactory +@JiraKey( "HHH-17634" ) +public class SimpleMergeTest { + + @Test + public void testMergeNewEntity(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + MyEntity newEntity = new MyEntity(); + + assertThat( newEntity.getId() ).isNull(); + + MyEntity mergedEntity = session.merge( newEntity ); + assertThat( mergedEntity ).isNotSameAs( newEntity ); + assertThat( mergedEntity.getId() ).isNotNull(); + assertThat( newEntity.getId() ).isNull(); + } + ); + } + + + @Entity(name = "MyEntity") + public static class MyEntity { + + @Id + @GeneratedValue + private Integer id; + + private String name; + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + } + +} + + From 529d4915ba6e52942dfccb79200c48e367066be7 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 10 Jan 2024 16:02:04 +0100 Subject: [PATCH 051/321] HHH-17634 Merging a new entity having a @GeneratedValue id should not set the generated id of the original entity --- .../internal/DefaultMergeEventListener.java | 143 +++++++++++++----- 1 file changed, 107 insertions(+), 36 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java index 7f4b42909340..83d686bc88d3 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java @@ -25,6 +25,7 @@ import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.spi.SelfDirtinessTracker; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.event.spi.EntityCopyObserver; import org.hibernate.event.spi.EventSource; @@ -40,8 +41,10 @@ import org.hibernate.proxy.LazyInitializer; import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.type.CollectionType; +import org.hibernate.type.CompositeType; import org.hibernate.type.EntityType; import org.hibernate.type.ForeignKeyDirection; +import org.hibernate.type.Type; import org.hibernate.type.TypeHelper; import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; @@ -145,12 +148,67 @@ private void doMerge(MergeEvent event, MergeContext copiedAlready, Object entity } private void merge(MergeEvent event, MergeContext copiedAlready, Object entity) { - switch ( entityState( event, entity ) ) { + final EventSource source = event.getSession(); + // Check the persistence context for an entry relating to this + // entity to be merged... + final PersistenceContext persistenceContext = source.getPersistenceContextInternal(); + EntityEntry entry = persistenceContext.getEntry( entity ); + final EntityState entityState; + final Object copiedId; + final Object originalId; + if ( entry == null ) { + final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity ); + originalId = persister.getIdentifier( entity, source ); + if ( originalId != null ) { + final EntityKey entityKey; + if ( persister.getIdentifierType().isComponentType() ) { + /* + this is needed in case of composite id containing an association with a generated identifier, in such a case + generating the EntityKey will cause a NPE when trying to get the hashcode of the null id + */ + copiedId = copyCompositeTypeId( + originalId, + (CompositeType) persister.getIdentifierType(), + source, + copiedAlready + ); + entityKey = source.generateEntityKey( copiedId, persister ); + } + else { + copiedId = null; + entityKey = source.generateEntityKey( originalId, persister ); + } + final Object managedEntity = persistenceContext.getEntity( entityKey ); + entry = persistenceContext.getEntry( managedEntity ); + if ( entry != null ) { + // we have a special case of a detached entity from the + // perspective of the merge operation. Specifically, we have + // an incoming entity instance which has a corresponding + // entry in the current persistence context, but registered + // under a different entity instance + entityState = EntityState.DETACHED; + } + else { + entityState = getEntityState( entity, event.getEntityName(), entry, source, false ); + } + } + else { + copiedId = null; + entityState = getEntityState( entity, event.getEntityName(), entry, source, false ); + } + } + else { + copiedId = null; + originalId = null; + entityState = getEntityState( entity, event.getEntityName(), entry, source, false ); + } + + switch ( entityState ) { case DETACHED: - entityIsDetached( event, copiedAlready ); + entityIsDetached( event, copiedId, originalId, copiedAlready ); break; case TRANSIENT: - entityIsTransient( event, copiedAlready ); + entityIsTransient( event, copiedId != null ? copiedId : originalId, copiedAlready ); break; case PERSISTENT: entityIsPersistent( event, copiedAlready ); @@ -166,7 +224,7 @@ private void merge(MergeEvent event, MergeContext copiedAlready, Object entity) ) ); event.getSession().getActionQueue().unScheduleUnloadedDeletion( entity ); - entityIsDetached(event, copiedAlready); + entityIsDetached(event, copiedId, originalId, copiedAlready); break; } throw new ObjectDeletedException( @@ -177,30 +235,37 @@ private void merge(MergeEvent event, MergeContext copiedAlready, Object entity) } } - private static EntityState entityState(MergeEvent event, Object entity) { - final EventSource source = event.getSession(); - // Check the persistence context for an entry relating to this - // entity to be merged... - final PersistenceContext persistenceContext = source.getPersistenceContextInternal(); - EntityEntry entry = persistenceContext.getEntry( entity ); - if ( entry == null ) { - EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity ); - Object id = persister.getIdentifier( entity, source ); - if ( id != null ) { - final EntityKey entityKey = source.generateEntityKey( id, persister ); - final Object managedEntity = persistenceContext.getEntity( entityKey ); - entry = persistenceContext.getEntry( managedEntity ); - if ( entry != null ) { - // we have a special case of a detached entity from the - // perspective of the merge operation. Specifically, we have - // an incoming entity instance which has a corresponding - // entry in the current persistence context, but registered - // under a different entity instance - return EntityState.DETACHED; + private static Object copyCompositeTypeId( + Object id, + CompositeType compositeType, + EventSource session, + MergeContext mergeContext) { + final SessionFactoryImplementor sessionFactory = session.getSessionFactory(); + final Object idCopy = compositeType.deepCopy( id, sessionFactory ); + final Type[] subtypes = compositeType.getSubtypes(); + final Object[] propertyValues = compositeType.getPropertyValues( id ); + final Object[] copyValues = compositeType.getPropertyValues( idCopy ); + for ( int i = 0; i < subtypes.length; i++ ) { + final Type subtype = subtypes[i]; + if ( subtype.isEntityType() ) { + // the value of the copy in the MergeContext has the id assigned + final Object o = mergeContext.get( propertyValues[i] ); + if ( o != null ) { + copyValues[i] = o; } + else { + copyValues[i] = subtype.deepCopy( propertyValues[i], sessionFactory ); + } + } + else if ( subtype.isComponentType() ) { + copyValues[i] = copyCompositeTypeId( propertyValues[i], (CompositeType) subtype, session, mergeContext ); + } + else { + copyValues[i] = subtype.deepCopy( propertyValues[i], sessionFactory ); } } - return getEntityState( entity, event.getEntityName(), entry, source, false ); + compositeType.setPropertyValues( idCopy, copyValues ); + return idCopy; } protected void entityIsPersistent(MergeEvent event, MergeContext copyCache) { @@ -215,14 +280,13 @@ protected void entityIsPersistent(MergeEvent event, MergeContext copyCache) { event.setResult( entity ); } - protected void entityIsTransient(MergeEvent event, MergeContext copyCache) { + protected void entityIsTransient(MergeEvent event, Object id, MergeContext copyCache) { LOG.trace( "Merging transient instance" ); final Object entity = event.getEntity(); final EventSource session = event.getSession(); final String entityName = event.getEntityName(); final EntityPersister persister = session.getEntityPersister( entityName, entity ); - final Object id = persister.getIdentifier( entity, session ); final Object copy = copyEntity( copyCache, entity, session, persister, id ); // cascade first, so that all unsaved objects get their @@ -232,7 +296,6 @@ protected void entityIsTransient(MergeEvent event, MergeContext copyCache) { copyValues( persister, entity, copy, session, copyCache, ForeignKeyDirection.FROM_PARENT ); saveTransientEntity( copy, entityName, event.getRequestedId(), session, copyCache ); - persister.setIdentifier( entity, persister.getIdentifier( copy, session ), session ); // cascade first, so that all unsaved objects get their // copy created before we actually copy @@ -319,17 +382,25 @@ private void saveTransientEntity( } } - protected void entityIsDetached(MergeEvent event, MergeContext copyCache) { + protected void entityIsDetached(MergeEvent event, Object copiedId, Object originalId, MergeContext copyCache) { LOG.trace( "Merging detached instance" ); final Object entity = event.getEntity(); final EventSource source = event.getSession(); final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity ); final String entityName = persister.getEntityName(); - - Object id = getDetachedEntityId( event, entity, persister ); + if ( originalId == null ) { + originalId = persister.getIdentifier( entity, source ); + } + final Object clonedIdentifier; + if ( copiedId == null ) { + clonedIdentifier = persister.getIdentifierType().deepCopy( originalId, source.getFactory() ); + } + else { + clonedIdentifier = copiedId; + } + final Object id = getDetachedEntityId( event, originalId, persister ); // we must clone embedded composite identifiers, or we will get back the same instance that we pass in - final Object clonedIdentifier = persister.getIdentifierType().deepCopy( id, source.getFactory() ); // apply the special MERGE fetch profile and perform the resolution (Session#get) final Object result = source.getLoadQueryInfluencers().fromInternalFetchProfile( CascadingFetchProfile.MERGE, @@ -344,7 +415,7 @@ protected void entityIsDetached(MergeEvent event, MergeContext copyCache) { // we got here because we assumed that an instance // with an assigned id was detached, when it was // really persistent - entityIsTransient( event, copyCache ); + entityIsTransient( event, clonedIdentifier, copyCache ); } else { // before cascade! @@ -386,15 +457,15 @@ else if ( isVersionChanged( entity, source, persister, target ) ) { } } - private static Object getDetachedEntityId(MergeEvent event, Object entity, EntityPersister persister) { + private static Object getDetachedEntityId(MergeEvent event, Object originalId, EntityPersister persister) { final EventSource source = event.getSession(); final Object id = event.getRequestedId(); if ( id == null ) { - return persister.getIdentifier( entity, source ); + return originalId; } else { // check that entity id = requestedId - final Object entityId = persister.getIdentifier( entity, source ); + final Object entityId = originalId; if ( !persister.getIdentifierType().isEqual( id, entityId, source.getFactory() ) ) { throw new HibernateException( "merge requested with id not matching id of passed entity" ); } From e5a2b3ec0b81f5604ef5e2d3b98ca6590e842772 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 05:11:08 +0000 Subject: [PATCH 052/321] Bump actions/cache from 3 to 4 Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/atlas.yml | 2 +- .github/workflows/contributor-build.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/atlas.yml b/.github/workflows/atlas.yml index e1ec067548df..941f531021ee 100644 --- a/.github/workflows/atlas.yml +++ b/.github/workflows/atlas.yml @@ -61,7 +61,7 @@ jobs: run: echo "yearmonth=$(/bin/date -u "+%Y-%m")" >> $GITHUB_OUTPUT shell: bash - name: Cache Maven local repository - uses: actions/cache@v3 + uses: actions/cache@v4 id: cache-maven with: path: | diff --git a/.github/workflows/contributor-build.yml b/.github/workflows/contributor-build.yml index fbaad2fa68a3..2cc1b2de6309 100644 --- a/.github/workflows/contributor-build.yml +++ b/.github/workflows/contributor-build.yml @@ -70,7 +70,7 @@ jobs: run: echo "yearmonth=$(/bin/date -u "+%Y-%m")" >> $GITHUB_OUTPUT shell: bash - name: Cache Maven local repository - uses: actions/cache@v3 + uses: actions/cache@v4 id: cache-maven with: path: | From a060a3b9c01ff5060c70bd5fcc10d6e889c615b3 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 10 Jan 2024 10:45:56 +0100 Subject: [PATCH 053/321] HHH-17598 Allow array typed queries without result wrapping --- .../internal/ConcreteSqmSelectQueryPlan.java | 71 ++++++++++--------- .../ConvertedListAttributeQueryTest.java | 18 ++++- 2 files changed, 53 insertions(+), 36 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java index 0dcc541a9c7f..70b11fe518e9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java @@ -195,18 +195,18 @@ protected static SqmJdbcExecutionContextAdapter listInterpreterExecutionContext( return new MySqmJdbcExecutionContextAdapter( executionContext, jdbcSelect, subSelectFetchKeyHandler, hql ); } - private static Class singleSelectionType(SqmSelectStatement sqm) { + private static SqmSelection singleSelection(SqmSelectStatement sqm) { final List> selections = sqm.getQueryPart() .getFirstQuerySpec() .getSelectClause() .getSelections(); - if ( selections.size() == 1 ) { - final SqmSelection sqmSelection = selections.get( 0 ); - return sqmSelection.getSelectableNode().isCompoundSelection() ? - null : - sqmSelection.getNodeJavaType().getJavaTypeClass(); - } - return null; + return selections.size() == 1 ? selections.get( 0 ) : null; + } + + private static Class selectionType(SqmSelection selection) { + return selection != null && !selection.getSelectableNode().isCompoundSelection() ? + selection.getNodeJavaType().getJavaTypeClass() + : null; } @SuppressWarnings("unchecked") @@ -221,37 +221,40 @@ protected static RowTransformer determineRowTransformer( else if ( resultType == null ) { return RowTransformerStandardImpl.instance(); } - else if ( resultType == Object[].class ) { - return (RowTransformer) RowTransformerArrayImpl.instance(); - } - else if ( resultType == List.class && resultType != singleSelectionType( sqm ) ) { - return (RowTransformer) RowTransformerListImpl.instance(); - } else { - // NOTE : if we get here : - // 1) there is no TupleTransformer specified - // 2) an explicit result-type, other than an array or List, was specified - - if ( tupleMetadata == null ) { - if ( sqm.getQueryPart().getFirstQuerySpec().getSelectClause().getSelections().size() == 1 ) { - return RowTransformerSingularReturnImpl.instance(); - } - else { - throw new AssertionFailure( "Query defined multiple selections, should have had TupleMetadata" ); - } + final SqmSelection selection = singleSelection( sqm ); + if ( resultType.isArray() && resultType != selectionType( selection ) ) { + return (RowTransformer) RowTransformerArrayImpl.instance(); + } + else if ( resultType == List.class && resultType != selectionType( selection ) ) { + return (RowTransformer) RowTransformerListImpl.instance(); } else { - if ( Tuple.class.equals( resultType ) ) { - return (RowTransformer) new RowTransformerJpaTupleImpl( tupleMetadata ); - } - else if ( Map.class.equals( resultType ) ) { - return (RowTransformer) new RowTransformerMapImpl( tupleMetadata ); - } - else if ( isClass( resultType ) ) { - return new RowTransformerConstructorImpl<>( resultType, tupleMetadata ); + // NOTE : if we get here : + // 1) there is no TupleTransformer specified + // 2) an explicit result-type, other than an array or List, was specified + + if ( tupleMetadata == null ) { + if ( selection != null ) { + return RowTransformerSingularReturnImpl.instance(); + } + else { + throw new AssertionFailure( "Query defined multiple selections, should have had TupleMetadata" ); + } } else { - throw new InstantiationException( "Query result type is not instantiable", resultType ); + if ( Tuple.class.equals( resultType ) ) { + return (RowTransformer) new RowTransformerJpaTupleImpl( tupleMetadata ); + } + else if ( Map.class.equals( resultType ) ) { + return (RowTransformer) new RowTransformerMapImpl( tupleMetadata ); + } + else if ( isClass( resultType ) ) { + return new RowTransformerConstructorImpl<>( resultType, tupleMetadata ); + } + else { + throw new InstantiationException( "Query result type is not instantiable", resultType ); + } } } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConvertedListAttributeQueryTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConvertedListAttributeQueryTest.java index 6fc1333adf44..41669c74de40 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConvertedListAttributeQueryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConvertedListAttributeQueryTest.java @@ -47,7 +47,7 @@ public void tearDown(SessionFactoryScope scope) { @Test @SuppressWarnings( "rawtypes" ) - public void testHQL(SessionFactoryScope scope) { + public void testListHQL(SessionFactoryScope scope) { scope.inTransaction( session -> { final List resultList = session.createQuery( "select emp.phoneNumbers from Employee emp where emp.id = :EMP_ID", @@ -59,7 +59,7 @@ public void testHQL(SessionFactoryScope scope) { @Test @SuppressWarnings( "rawtypes" ) - public void testCriteria(SessionFactoryScope scope) { + public void testListCriteria(SessionFactoryScope scope) { scope.inTransaction( session -> { final CriteriaBuilder cb = session.getCriteriaBuilder(); final CriteriaQuery q = cb.createQuery( List.class ); @@ -71,6 +71,20 @@ public void testCriteria(SessionFactoryScope scope) { } ); } + @Test + public void testArrayCriteria(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final CriteriaBuilder cb = session.getCriteriaBuilder(); + final CriteriaQuery q = cb.createQuery( Integer[].class ); + final Root r = q.from( Employee.class ); + q.multiselect( r.get( "id" ), r.get( "id" ) ); + q.where( cb.equal( r.get( "id" ), 1 ) ); + final Object result = session.createQuery( q ).getSingleResult(); + assertThat( result ).isInstanceOf( Object[].class ); + assertThat( ( (Object[]) result ) ).containsExactly( 1, 1 ); + } ); + } + @Entity( name = "Employee" ) public static class Employee { @Id From 641afbcf84b6f20f4d7e0ab541ff68c91e34a413 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 20 Dec 2023 11:58:34 +0100 Subject: [PATCH 054/321] HHH-17550 Add test for issue --- .../disabled/DefaultConstraintModeTest.java | 71 ++++++++++++------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/foreignkeys/disabled/DefaultConstraintModeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/foreignkeys/disabled/DefaultConstraintModeTest.java index 8830f4c6c81a..ec5e49a91295 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/foreignkeys/disabled/DefaultConstraintModeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/foreignkeys/disabled/DefaultConstraintModeTest.java @@ -6,69 +6,90 @@ */ package org.hibernate.orm.test.foreignkeys.disabled; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - +import java.util.List; import java.util.stream.StreamSupport; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; - import org.hibernate.boot.Metadata; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.cfg.Environment; import org.hibernate.mapping.Table; -import org.hibernate.testing.TestForIssue; + import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.util.ServiceRegistryUtil; import org.junit.Test; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + /** * @author Yanming Zhou */ +@Jira( "https://hibernate.atlassian.net/browse/HHH-14253" ) +@Jira( "https://hibernate.atlassian.net/browse/HHH-17550" ) public class DefaultConstraintModeTest extends BaseUnitTestCase { - - private static final String TABLE_NAME = "TestEntity"; - @Test - @TestForIssue(jiraKey = "HHH-14253") public void testForeignKeyShouldNotBeCreated() { - testForeignKeyCreation(false); + testForeignKeyCreation( false ); } @Test - @TestForIssue(jiraKey = "HHH-14253") public void testForeignKeyShouldBeCreated() { - testForeignKeyCreation(true); + testForeignKeyCreation( true ); } private void testForeignKeyCreation(boolean created) { try (StandardServiceRegistry ssr = ServiceRegistryUtil.serviceRegistryBuilder() - .applySetting(Environment.HBM2DDL_DEFAULT_CONSTRAINT_MODE, created ? "CONSTRAINT" : "NO_CONSTRAINT").build()) { - Metadata metadata = new MetadataSources( ssr ).addAnnotatedClass( TestEntity.class ).buildMetadata(); - assertThat( findTable( metadata, TABLE_NAME ).getForeignKeys().isEmpty(), is( !created ) ); + .applySetting( Environment.HBM2DDL_DEFAULT_CONSTRAINT_MODE, created ? "CONSTRAINT" : "NO_CONSTRAINT" ) + .build()) { + Metadata metadata = new MetadataSources( ssr ).addAnnotatedClasses( TestEntity.class, ChildEntity.class ).buildMetadata(); + assertThat( findTable( metadata, "TestEntity" ).getForeignKeys().isEmpty(), is( !created ) ); + assertThat( findTable( metadata, "ChildEntity" ).getForeignKeys().isEmpty(), is( !created ) ); } } private static Table findTable(Metadata metadata, String tableName) { - return StreamSupport.stream(metadata.getDatabase().getNamespaces().spliterator(), false) - .flatMap(namespace -> namespace.getTables().stream()).filter(t -> t.getName().equals(tableName)) - .findFirst().orElse(null); + return StreamSupport.stream( metadata.getDatabase().getNamespaces().spliterator(), false ) + .flatMap( namespace -> namespace.getTables().stream() ).filter( t -> t.getName().equals( tableName ) ) + .findFirst().orElse( null ); } - @Entity - @jakarta.persistence.Table(name = TABLE_NAME) + @Entity( name = "TestEntity" ) + @jakarta.persistence.Table( name = "TestEntity" ) + @Inheritance( strategy = InheritanceType.JOINED ) public static class TestEntity { @Id private Long id; @ManyToOne + private TestEntity toOne; + + @OneToMany @JoinColumn - private TestEntity mate; + private List oneToMany; + + @ManyToMany + private List manyToMany; + + @ElementCollection + private List elements; + } + @Entity( name = "ChildEntity" ) + @jakarta.persistence.Table( name = "ChildEntity" ) + public static class ChildEntity extends TestEntity { + private String childName; } } From bdb2906a9440555e456376df41ff904141ee8040 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 20 Dec 2023 11:58:43 +0100 Subject: [PATCH 055/321] HHH-17550 Respect default no-constraint setting --- .../boot/model/internal/BinderHelper.java | 15 +++++ .../boot/model/internal/EntityBinder.java | 57 ++++++++++--------- .../boot/model/internal/ToOneBinder.java | 22 +++---- 3 files changed, 54 insertions(+), 40 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java index a551f4c2ccbb..7f97fa77fce8 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java @@ -62,12 +62,16 @@ import org.hibernate.mapping.Value; import org.hibernate.type.descriptor.java.JavaType; +import jakarta.persistence.ConstraintMode; import jakarta.persistence.Embeddable; import jakarta.persistence.EmbeddedId; import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToOne; +import static jakarta.persistence.ConstraintMode.NO_CONSTRAINT; +import static jakarta.persistence.ConstraintMode.PROVIDER_DEFAULT; import static org.hibernate.boot.model.internal.AnnotatedColumn.buildColumnOrFormulaFromAnnotation; import static org.hibernate.boot.model.internal.HCANNHelper.findAnnotation; import static org.hibernate.internal.util.StringHelper.isEmpty; @@ -1124,6 +1128,17 @@ private static boolean checkReferencedClass(PersistentClass ownerClass, Persiste return false; } + public static boolean noConstraint(ForeignKey foreignKey, boolean noConstraintByDefault) { + if ( foreignKey == null ) { + return false; + } + else { + final ConstraintMode mode = foreignKey.value(); + return mode == NO_CONSTRAINT + || mode == PROVIDER_DEFAULT && noConstraintByDefault; + } + } + /** * Extract an annotation from the package-info for the package the given class is defined in * diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java index d6e913d48c61..b9d398b4c2d1 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java @@ -31,7 +31,6 @@ import org.hibernate.annotations.DynamicUpdate; import org.hibernate.annotations.Filter; import org.hibernate.annotations.Filters; -import org.hibernate.annotations.ForeignKey; import org.hibernate.annotations.HQLSelect; import org.hibernate.annotations.Immutable; import org.hibernate.annotations.Loader; @@ -117,6 +116,7 @@ import jakarta.persistence.DiscriminatorColumn; import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; +import jakarta.persistence.ForeignKey; import jakarta.persistence.GeneratedValue; import jakarta.persistence.IdClass; import jakarta.persistence.Inheritance; @@ -139,6 +139,7 @@ import static org.hibernate.boot.model.internal.BinderHelper.getOverridableAnnotation; import static org.hibernate.boot.model.internal.BinderHelper.hasToOneAnnotation; import static org.hibernate.boot.model.internal.BinderHelper.isDefault; +import static org.hibernate.boot.model.internal.BinderHelper.noConstraint; import static org.hibernate.boot.model.internal.BinderHelper.toAliasEntityMap; import static org.hibernate.boot.model.internal.BinderHelper.toAliasTableMap; import static org.hibernate.boot.model.internal.EmbeddableBinder.fillEmbeddable; @@ -843,34 +844,38 @@ private static void checkNoJoinColumns(XClass clazzToProcess) { } private static void handleForeignKeys(XClass clazzToProcess, MetadataBuildingContext context, DependantValue key) { - final ForeignKey foreignKey = clazzToProcess.getAnnotation( ForeignKey.class ); - if ( foreignKey != null && !foreignKey.name().isEmpty() ) { - key.setForeignKeyName( foreignKey.name() ); + final PrimaryKeyJoinColumn pkJoinColumn = clazzToProcess.getAnnotation( PrimaryKeyJoinColumn.class ); + final PrimaryKeyJoinColumns pkJoinColumns = clazzToProcess.getAnnotation( PrimaryKeyJoinColumns.class ); + final boolean noConstraintByDefault = context.getBuildingOptions().isNoConstraintByDefault(); + if ( pkJoinColumn != null && noConstraint( pkJoinColumn.foreignKey(), noConstraintByDefault ) + || pkJoinColumns != null && noConstraint( pkJoinColumns.foreignKey(), noConstraintByDefault ) ) { + key.disableForeignKey(); } else { - final PrimaryKeyJoinColumn pkJoinColumn = clazzToProcess.getAnnotation( PrimaryKeyJoinColumn.class ); - final PrimaryKeyJoinColumns pkJoinColumns = clazzToProcess.getAnnotation( PrimaryKeyJoinColumns.class ); - final boolean noConstraintByDefault = context.getBuildingOptions().isNoConstraintByDefault(); - if ( pkJoinColumns != null && ( pkJoinColumns.foreignKey().value() == ConstraintMode.NO_CONSTRAINT - || pkJoinColumns.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) ) { - // don't apply a constraint based on ConstraintMode - key.disableForeignKey(); - } - else if ( pkJoinColumns != null && isNotEmpty( pkJoinColumns.foreignKey().name() ) ) { - key.setForeignKeyName( pkJoinColumns.foreignKey().name() ); - if ( !pkJoinColumns.foreignKey().foreignKeyDefinition().isEmpty() ) { - key.setForeignKeyDefinition( pkJoinColumns.foreignKey().foreignKeyDefinition() ); - } - } - else if ( pkJoinColumn != null && ( pkJoinColumn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT - || pkJoinColumn.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) ) { - // don't apply a constraint based on ConstraintMode - key.disableForeignKey(); + final org.hibernate.annotations.ForeignKey fk = + clazzToProcess.getAnnotation( org.hibernate.annotations.ForeignKey.class ); + if ( fk != null && isNotEmpty( fk.name() ) ) { + key.setForeignKeyName( fk.name() ); } - else if ( pkJoinColumn != null && isNotEmpty( pkJoinColumn.foreignKey().name() ) ) { - key.setForeignKeyName( pkJoinColumn.foreignKey().name() ); - if ( !pkJoinColumn.foreignKey().foreignKeyDefinition().isEmpty() ) { - key.setForeignKeyDefinition( pkJoinColumn.foreignKey().foreignKeyDefinition() ); + else { + final ForeignKey foreignKey = clazzToProcess.getAnnotation( ForeignKey.class ); + if ( noConstraint( foreignKey, noConstraintByDefault ) ) { + key.disableForeignKey(); + } + else if ( foreignKey != null ) { + key.setForeignKeyName( nullIfEmpty( foreignKey.name() ) ); + key.setForeignKeyDefinition( nullIfEmpty( foreignKey.foreignKeyDefinition() ) ); + } + else if ( noConstraintByDefault ) { + key.disableForeignKey(); + } + else if ( pkJoinColumns != null ) { + key.setForeignKeyName( nullIfEmpty( pkJoinColumns.foreignKey().name() ) ); + key.setForeignKeyDefinition( nullIfEmpty( pkJoinColumns.foreignKey().foreignKeyDefinition() ) ); + } + else if ( pkJoinColumn != null ) { + key.setForeignKeyName( nullIfEmpty( pkJoinColumn.foreignKey().name() ) ); + key.setForeignKeyDefinition( nullIfEmpty( pkJoinColumn.foreignKey().foreignKeyDefinition() ) ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ToOneBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ToOneBinder.java index 6ff94c0abec4..77dc9d1fc1db 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ToOneBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ToOneBinder.java @@ -57,6 +57,7 @@ import static org.hibernate.boot.model.internal.BinderHelper.getFetchMode; import static org.hibernate.boot.model.internal.BinderHelper.getPath; import static org.hibernate.boot.model.internal.BinderHelper.isDefault; +import static org.hibernate.boot.model.internal.BinderHelper.noConstraint; import static org.hibernate.internal.CoreLogging.messageLogger; import static org.hibernate.internal.util.StringHelper.isEmpty; import static org.hibernate.internal.util.StringHelper.isNotEmpty; @@ -610,8 +611,9 @@ public static void bindForeignKeyNameAndDefinition( else { final JoinColumn joinColumn = property.getAnnotation( JoinColumn.class ); final JoinColumns joinColumns = property.getAnnotation( JoinColumns.class ); - if ( joinColumn!=null && noConstraint( joinColumn.foreignKey(), context ) - || joinColumns!=null && noConstraint( joinColumns.foreignKey(), context ) ) { + final boolean noConstraintByDefault = context.getBuildingOptions().isNoConstraintByDefault(); + if ( joinColumn != null && noConstraint( joinColumn.foreignKey(), noConstraintByDefault ) + || joinColumns != null && noConstraint( joinColumns.foreignKey(), noConstraintByDefault ) ) { value.disableForeignKey(); } else { @@ -621,13 +623,16 @@ public static void bindForeignKeyNameAndDefinition( value.setForeignKeyName( fk.name() ); } else { - if ( noConstraint( foreignKey, context ) ) { + if ( noConstraint( foreignKey, noConstraintByDefault ) ) { value.disableForeignKey(); } else if ( foreignKey != null ) { value.setForeignKeyName( nullIfEmpty( foreignKey.name() ) ); value.setForeignKeyDefinition( nullIfEmpty( foreignKey.foreignKeyDefinition() ) ); } + else if ( noConstraintByDefault ) { + value.disableForeignKey(); + } else if ( joinColumns != null ) { value.setForeignKeyName( nullIfEmpty( joinColumns.foreignKey().name() ) ); value.setForeignKeyDefinition( nullIfEmpty( joinColumns.foreignKey().foreignKeyDefinition() ) ); @@ -641,17 +646,6 @@ else if ( joinColumn != null ) { } } - private static boolean noConstraint(ForeignKey joinColumns, MetadataBuildingContext context) { - if ( joinColumns == null ) { - return false; - } - else { - final ConstraintMode mode = joinColumns.value(); - return mode == NO_CONSTRAINT - || mode == PROVIDER_DEFAULT && context.getBuildingOptions().isNoConstraintByDefault(); - } - } - public static String getReferenceEntityName(PropertyData propertyData, XClass targetEntity, MetadataBuildingContext context) { return isDefault( targetEntity, context ) ? propertyData.getClassOrElementName() From f33afc66bf5350fd7f42f4731f24dab125f5b39d Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 23 Jan 2024 13:24:34 +0100 Subject: [PATCH 056/321] HHH-17644 Add test for issue --- .../SingleTableAndGenericsTest.java | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/SingleTableAndGenericsTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/SingleTableAndGenericsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/SingleTableAndGenericsTest.java new file mode 100644 index 000000000000..c5a7eaf0cc4f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/SingleTableAndGenericsTest.java @@ -0,0 +1,95 @@ +package org.hibernate.orm.test.inheritance.discriminator; + +import org.hibernate.annotations.JdbcTypeCode; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.Table; + +import static jakarta.persistence.InheritanceType.SINGLE_TABLE; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.hibernate.type.SqlTypes.JSON; + +@DomainModel( + annotatedClasses = { + // the order is important to reproduce the issue + SingleTableAndGenericsTest.B.class, + SingleTableAndGenericsTest.A.class, + } +) +@SessionFactory +@JiraKey("HHH-17644") +public class SingleTableAndGenericsTest { + + @Test + public void testIt(SessionFactoryScope scope) { + String payload = "{\"book\": \"1\"}"; + String aId = "1"; + + scope.inTransaction( + session -> { + A a = new A(); + a.setId( aId ); + session.persist( a ); + a.setPayload( payload ); + } + ); + + scope.inTransaction( + session -> { + A a = session.find( A.class, aId ); + assertThat( a ).isNotNull(); + String payload1 = a.getPayload(); + assertThat( payload1 ).isNotNull(); + assertThat( payload1 ).contains( "book" ); + } + ); + } + + @Entity(name = "B") + @Table(name = "table_b") + @Inheritance(strategy = SINGLE_TABLE) + @DiscriminatorColumn(name = "demo_type") + public static abstract class B { + + @Id + @Column(name = "id", nullable = false) + private String id; + + @Column(name = "payload") + @JdbcTypeCode(JSON) + private T payload; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public T getPayload() { + return payload; + } + + public void setPayload(T payload) { + this.payload = payload; + } + } + + @Entity(name = "C") + @DiscriminatorValue("child") + public static class A extends B { + } +} + From 9201ee7109ea3009cc94cfe83832afccd2497f8f Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 23 Jan 2024 13:26:16 +0100 Subject: [PATCH 057/321] HHH-17644 Mapping of generic types in single table inheritance depends on lexicographical order of parent and child classnames --- .../org/hibernate/boot/model/internal/PropertyContainer.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyContainer.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyContainer.java index 2b0083c31819..3248eee16f21 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyContainer.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyContainer.java @@ -19,6 +19,7 @@ import org.hibernate.AnnotationException; import org.hibernate.annotations.JavaType; +import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.annotations.ManyToAny; import org.hibernate.annotations.Target; import org.hibernate.annotations.Type; @@ -436,6 +437,9 @@ else if ( property.isAnnotationPresent( Type.class ) ) { else if ( property.isAnnotationPresent( JavaType.class ) ) { return true; } + else if ( property.isAnnotationPresent( JdbcTypeCode.class ) ) { + return true; + } else if ( property.isAnnotationPresent( Target.class ) ) { return true; } From aef88b134d735212263243d2c2844b06127d1421 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 22 Jan 2024 11:53:47 +0100 Subject: [PATCH 058/321] HHH-17420 Add test for issue --- .../annotations/onetoone/JoinColumnTest.java | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/onetoone/JoinColumnTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/onetoone/JoinColumnTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/onetoone/JoinColumnTest.java new file mode 100644 index 000000000000..b83a31a9fe1a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/onetoone/JoinColumnTest.java @@ -0,0 +1,99 @@ +package org.hibernate.orm.test.annotations.onetoone; + +import java.io.Serializable; + +import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; + +@JiraKey("HHH-17420") +@Jpa( + annotatedClasses = { + JoinColumnTest.LeftEntity.class, + JoinColumnTest.MidEntity.class, + JoinColumnTest.RightEntity.class, + } +) +class JoinColumnTest extends BaseUnitTestCase { + + @Test + void testLeftToRight(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + } + ); + } + + @Entity(name = "LeftEntity") + static class LeftEntity { + + @Embeddable + static class Pk implements Serializable { + @Column + String id_one_2; + + @Column + String id_two_2; + + @Column + String id_three_2; + } + + @EmbeddedId + Pk id; + + @OneToOne(mappedBy = "aLeftEntity") + MidEntity midEntity; + } + + @Entity(name = "MidEntity") + static class MidEntity { + + @Id + @Column + String id; + + @Column + String id_one; + + @Column + String id_two; + + @Column + String id_three; + + @OneToOne + @JoinColumn(name = "id_one", referencedColumnName = "id_one_2", insertable = false, updatable = false) + @JoinColumn(name = "id_two", referencedColumnName = "id_two_2", insertable = false, updatable = false) + @JoinColumn(name = "id_three", referencedColumnName = "id_three_2", insertable = false, updatable = false) + LeftEntity aLeftEntity; + + @OneToOne(mappedBy = "midEntity") + RightEntity rightEntity; + } + + @Entity(name = "RightEntity") + static class RightEntity { + + @Id + Long id; + + @Column + String id_three; + + @OneToOne + @JoinColumn(name = "id_one", referencedColumnName = "id_one", insertable = false, updatable = false) + @JoinColumn(name = "id_two", referencedColumnName = "id_two", insertable = false, updatable = false) + MidEntity midEntity; + } +} From 316c7af186dad9d4a31a40cbc1cd69aef757f496 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 22 Jan 2024 14:47:41 +0100 Subject: [PATCH 059/321] HHH-17420 JoinColumn throws an AnnotationException --- .../boot/model/internal/BinderHelper.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java index 7f97fa77fce8..2cfa602dc0e6 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java @@ -452,9 +452,16 @@ else if ( columnOwner instanceof Join ) { // which are mapped to that column. (There might be multiple such // properties for each column.) if ( columnOwner instanceof PersistentClass ) { - PersistentClass persistentClass = (PersistentClass) columnOwner; + final PersistentClass persistentClass = (PersistentClass) columnOwner; + // Process ToOne associations after Components, Basic and Id properties + final List toOneProperties = new ArrayList<>(); for ( Property property : persistentClass.getReferenceableProperties() ) { - matchColumnsByProperty( property, columnsToProperty ); + if ( property.getValue() instanceof ToOne ) { + toOneProperties.add( property ); + } + else { + matchColumnsByProperty( property, columnsToProperty ); + } } if ( persistentClass.hasIdentifierProperty() ) { matchColumnsByProperty( persistentClass.getIdentifierProperty(), columnsToProperty ); @@ -466,6 +473,9 @@ else if ( columnOwner instanceof Join ) { matchColumnsByProperty( p, columnsToProperty ); } } + for ( Property property : toOneProperties ) { + matchColumnsByProperty( property, columnsToProperty ); + } } else { for ( Property property : ((Join) columnOwner).getProperties() ) { From ce0217df461ca902daa773cb35772e9fd0b20ae4 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 23 Jan 2024 15:00:56 +0100 Subject: [PATCH 060/321] HHH-17667 Add test for issue --- ...ritanceDiscriminatorRemovalByTypeTest.java | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/JoinedInheritanceDiscriminatorRemovalByTypeTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/JoinedInheritanceDiscriminatorRemovalByTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/JoinedInheritanceDiscriminatorRemovalByTypeTest.java new file mode 100644 index 000000000000..d35bb6e57fdc --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/JoinedInheritanceDiscriminatorRemovalByTypeTest.java @@ -0,0 +1,132 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.orm.test.inheritance; + +import java.io.Serializable; +import java.util.List; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.orm.test.inheritance.JoinedInheritanceDiscriminatorRemovalByTypeTest.BaseEntity; +import static org.hibernate.orm.test.inheritance.JoinedInheritanceDiscriminatorRemovalByTypeTest.BaseVehicle; +import static org.hibernate.orm.test.inheritance.JoinedInheritanceDiscriminatorRemovalByTypeTest.Car; +import static org.hibernate.orm.test.inheritance.JoinedInheritanceDiscriminatorRemovalByTypeTest.Truck; + +@DomainModel( annotatedClasses = { Car.class, Truck.class, BaseVehicle.class, BaseEntity.class, } ) +@SessionFactory +@Jira( "https://hibernate.atlassian.net/browse/HHH-17667" ) +public class JoinedInheritanceDiscriminatorRemovalByTypeTest { + @BeforeEach + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.persist( Car.create( 1L ) ); + session.persist( Car.create( 2L ) ); + session.persist( Car.create( 3L ) ); + session.persist( Truck.create( 4L ) ); + session.persist( Truck.create( 5L ) ); + } ); + } + + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createMutationQuery( "delete from BaseEntity" ).executeUpdate() ); + } + + @Test + public void testInPredicateParameter(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createMutationQuery( + "delete from BaseVehicle e where type(e) in(:type)" + ).setParameter( "type", Car.class ).executeUpdate() ); + assertRemaining( scope, 4L, 5L ); + } + + @Test + public void testInPredicateLiteral(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createMutationQuery( + "delete from BaseVehicle e where type(e) in(Truck)" + ).executeUpdate() ); + assertRemaining( scope, 1L, 2L, 3L ); + } + + @Test + public void testComparisonPredicateParameter(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createMutationQuery( + "delete from BaseVehicle e where type(e) = :type" + ).setParameter( "type", Truck.class ).executeUpdate() ); + assertRemaining( scope, 1L, 2L, 3L ); + } + + @Test + public void testComparisonPredicateLiteral(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createMutationQuery( + "delete from BaseVehicle e where type(e) = Car" + ).executeUpdate() ); + assertRemaining( scope, 4L, 5L ); + } + + private void assertRemaining(SessionFactoryScope scope, Long... ids) { + scope.inSession( session -> { + final List resultList = session.createQuery( + "from BaseVehicle ", + BaseVehicle.class + ).getResultList(); + assertThat( resultList ).hasSize( ids.length ); + assertThat( resultList.stream().map( BaseEntity::getId ) ).containsOnly( ids ); + } ); + } + + @Entity( name = "Car" ) + public static class Car extends BaseVehicle { + public static Car create(Long id) { + final Car car = new Car(); + car.setId( id ); + return car; + } + } + + @Entity( name = "Truck" ) + public static class Truck extends BaseVehicle { + public static Truck create(Long id) { + final Truck truck = new Truck(); + truck.setId( id ); + return truck; + } + } + + @Entity( name = "BaseVehicle" ) + public static abstract class BaseVehicle extends BaseEntity { + } + + @Entity( name = "BaseEntity" ) + @DiscriminatorColumn( name = "disc_col" ) + @Inheritance( strategy = InheritanceType.JOINED ) + public static abstract class BaseEntity implements Serializable { + @Id + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + } +} From 236e3e222846092171232725b80646c4371d697a Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 23 Jan 2024 15:00:17 +0100 Subject: [PATCH 061/321] HHH-17667 Fix pruning of root table with discriminator predicate --- .../sqm/sql/BaseSqmToSqlAstConverter.java | 102 +++++++++--------- 1 file changed, 48 insertions(+), 54 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index b79d20a4869e..e3d686786df3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -93,7 +93,6 @@ import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.persister.entity.EntityNameUse; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.entity.SingleTableEntityPersister; import org.hibernate.query.BindableType; import org.hibernate.query.QueryLogging; import org.hibernate.query.ReturnableType; @@ -3167,9 +3166,8 @@ protected void registerTypeUsage(TableGroup tableGroup) { registerEntityNameUsage( tableGroup, EntityNameUse.PROJECTION, persister.getEntityName(), true ); } else { - // Avoid doing this for single table entity persisters, as the table span includes secondary tables, - // which we don't want to resolve, though we know that there is only a single table anyway - if ( persister instanceof SingleTableEntityPersister ) { + // Avoid resolving subclass tables for persisters with physical discriminators as we won't need them + if ( persister.getDiscriminatorMapping().hasPhysicalColumn() ) { return; } final int subclassTableSpan = persister.getSubclassTableSpan(); @@ -7557,46 +7555,56 @@ else if ( rhs instanceof DiscriminatorPathInterpretation ) { else { return; } - if ( literalExpression == null ) { - // We have to assume all types are possible and can't do optimizations - final TableGroup tableGroup = getFromClauseIndex().getTableGroup( typeExpression.getNavigablePath().getParent() ); - final EntityMappingType entityMappingType = (EntityMappingType) tableGroup.getModelPart().getPartMappingType(); - registerEntityNameUsage( tableGroup, EntityNameUse.FILTER, entityMappingType.getEntityName() ); - for ( EntityMappingType subMappingType : entityMappingType.getSubMappingTypes() ) { - registerEntityNameUsage( tableGroup, EntityNameUse.FILTER, subMappingType.getEntityName() ); - } - } - else { - handleTypeComparison( typeExpression, Collections.singletonList( literalExpression ), inclusive ); - } + handleTypeComparison( + typeExpression, + literalExpression != null ? singletonList( literalExpression ) : null, + inclusive + ); } private void handleTypeComparison( - DiscriminatorPathInterpretation typeExpression, + DiscriminatorPathInterpretation typeExpression, List literalExpressions, boolean inclusive) { final TableGroup tableGroup = getFromClauseIndex().getTableGroup( typeExpression.getNavigablePath().getParent() ); - if ( inclusive ) { - for ( EntityTypeLiteral literalExpr : literalExpressions ) { - registerEntityNameUsage( - tableGroup, - EntityNameUse.FILTER, - literalExpr.getEntityTypeDescriptor().getEntityName() - ); + final EntityMappingType entityMappingType = (EntityMappingType) tableGroup.getModelPart().getPartMappingType(); + if ( entityMappingType.getDiscriminatorMapping().hasPhysicalColumn() ) { + // Prevent pruning of the root type's table reference containing the physical discriminator column + registerEntityNameUsage( + tableGroup, + EntityNameUse.EXPRESSION, + entityMappingType.getRootEntityDescriptor().getEntityName() + ); + } + if ( literalExpressions == null ) { + // We have to assume all types are possible and can't do optimizations + registerEntityNameUsage( tableGroup, EntityNameUse.FILTER, entityMappingType.getEntityName() ); + for ( EntityMappingType subMappingType : entityMappingType.getSubMappingTypes() ) { + registerEntityNameUsage( tableGroup, EntityNameUse.FILTER, subMappingType.getEntityName() ); } } else { - final EntityMappingType entityMappingType = (EntityMappingType) tableGroup.getModelPart().getPartMappingType(); - final Set excludedEntityNames = new HashSet<>(entityMappingType.getSubMappingTypes().size()); - for ( EntityTypeLiteral literalExpr : literalExpressions ) { - excludedEntityNames.add( literalExpr.getEntityTypeDescriptor().getEntityName() ); - } - if ( !excludedEntityNames.contains( entityMappingType.getEntityName() ) ) { - registerEntityNameUsage( tableGroup, EntityNameUse.FILTER, entityMappingType.getEntityName() ); + if ( inclusive ) { + for ( EntityTypeLiteral literalExpr : literalExpressions ) { + registerEntityNameUsage( + tableGroup, + EntityNameUse.FILTER, + literalExpr.getEntityTypeDescriptor().getEntityName() + ); + } } - for ( EntityMappingType subMappingType : entityMappingType.getSubMappingTypes() ) { - if ( !excludedEntityNames.contains( subMappingType.getEntityName() ) ) { - registerEntityNameUsage( tableGroup, EntityNameUse.FILTER, subMappingType.getEntityName() ); + else { + final Set excludedEntityNames = new HashSet<>( entityMappingType.getSubMappingTypes().size() ); + for ( EntityTypeLiteral literalExpr : literalExpressions ) { + excludedEntityNames.add( literalExpr.getEntityTypeDescriptor().getEntityName() ); + } + if ( !excludedEntityNames.contains( entityMappingType.getEntityName() ) ) { + registerEntityNameUsage( tableGroup, EntityNameUse.FILTER, entityMappingType.getEntityName() ); + } + for ( EntityMappingType subMappingType : entityMappingType.getSubMappingTypes() ) { + if ( !excludedEntityNames.contains( subMappingType.getEntityName() ) ) { + registerEntityNameUsage( tableGroup, EntityNameUse.FILTER, subMappingType.getEntityName() ); + } } } } @@ -7852,26 +7860,12 @@ private void handleTypeComparison(InListPredicate inPredicate) { break; } } - if ( containsNonLiteral ) { - // We have to assume all types are possible and can't do optimizations - final TableGroup tableGroup = getFromClauseIndex().getTableGroup( - typeExpression.getNavigablePath().getParent() - ); - final EntityMappingType entityMappingType = (EntityMappingType) tableGroup.getModelPart() - .getPartMappingType(); - registerEntityNameUsage( tableGroup, EntityNameUse.FILTER, entityMappingType.getEntityName() ); - for ( EntityMappingType subMappingType : entityMappingType.getSubMappingTypes() ) { - registerEntityNameUsage( tableGroup, EntityNameUse.FILTER, subMappingType.getEntityName() ); - } - } - else { - //noinspection unchecked - handleTypeComparison( - typeExpression, - (List) (List) inPredicate.getListExpressions(), - !inPredicate.isNegated() - ); - } + //noinspection unchecked + handleTypeComparison( + typeExpression, + containsNonLiteral ? null : (List) (List) inPredicate.getListExpressions(), + !inPredicate.isNegated() + ); } } From f79673d2741975fdf8c6e2a3ea424d854a240fc1 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Tue, 23 Jan 2024 18:45:58 +0100 Subject: [PATCH 062/321] HHH-17670 Test and fix for NPE in FromClause#findTableGroup --- .../sql/ast/tree/from/FromClause.java | 2 +- .../batch/BatchSubselectCollection3Test.java | 112 ++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/batch/BatchSubselectCollection3Test.java diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/FromClause.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/FromClause.java index 0e673eb7800b..99d8680790f6 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/FromClause.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/FromClause.java @@ -232,7 +232,7 @@ public TableGroup findTableGroup(NavigablePath navigablePath) { if ( navigablePath.equals( tg.getNavigablePath() ) ) { return tg; } - if ( tg instanceof OneToManyTableGroup && navigablePath.getParent().equals( tg.getNavigablePath() ) ) { + if ( tg instanceof OneToManyTableGroup && tg.getNavigablePath().equals( navigablePath.getParent() ) ) { return ( (OneToManyTableGroup) tg ).getTableGroup( CollectionPart.Nature.fromName( navigablePath.getLocalName() ) ); } return null; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/batch/BatchSubselectCollection3Test.java b/hibernate-core/src/test/java/org/hibernate/orm/test/batch/BatchSubselectCollection3Test.java new file mode 100644 index 000000000000..397cda48810e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/batch/BatchSubselectCollection3Test.java @@ -0,0 +1,112 @@ +package org.hibernate.orm.test.batch; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; +import org.hibernate.cfg.AvailableSettings; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; + +@DomainModel( + annotatedClasses = { + BatchSubselectCollection3Test.SimpleA.class, + BatchSubselectCollection3Test.SimpleB.class, + BatchSubselectCollection3Test.SimpleC.class, + BatchSubselectCollection3Test.SimpleD.class + } +) +@SessionFactory +@ServiceRegistry( + settings = { + @Setting(name = AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, value = "10"), + @Setting(name = AvailableSettings.FORMAT_SQL, value = "false"), + } +) +@JiraKey("HHH-17670") +public class BatchSubselectCollection3Test { + + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.persist( new SimpleA( 1L ) ); + session.persist( new SimpleA( 2L ) ); + + session.persist( new SimpleC( 1L ) ); + session.persist( new SimpleC( 2L ) ); + } + ); + } + + @Test + public void testQuery(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + // To trigger the NPE, it is vital to have a OneToManyTableGroup before the table group that is selected + session.createQuery( "select obj2 from SimpleA obj left outer join obj.children obj1, SimpleC obj2" ) + .getResultList(); + } + ); + } + + @Entity(name = "SimpleA") + public static class SimpleA { + @Id + private Long id; + @OneToMany(mappedBy = "parent") + private List children = new ArrayList<>(); + + public SimpleA() { + } + + public SimpleA(Long id) { + this.id = id; + } + } + + @Entity(name = "SimpleB") + public static class SimpleB { + @Id + private Long id; + @ManyToOne + private SimpleA parent; + } + + @Entity(name = "SimpleC") + public static class SimpleC { + @Id + private Long id; + private String s; + @OneToMany + @Fetch(FetchMode.SUBSELECT) + private List children2 = new ArrayList<>(); + + public SimpleC() { + } + + public SimpleC(Long id) { + this.id = id; + } + } + + @Entity(name = "SimpleD") + public static class SimpleD { + @Id + private Long id; + private String s; + } +} From a73bc08c78b203be5a19806a9004ae3a20552360 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 22 Jan 2024 16:33:26 +0100 Subject: [PATCH 063/321] HHH-16960 Add test for issue --- .../entitygraph/FindWithEntityGraphTest.java | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/FindWithEntityGraphTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/FindWithEntityGraphTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/FindWithEntityGraphTest.java new file mode 100644 index 000000000000..c0cec22aedcb --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/FindWithEntityGraphTest.java @@ -0,0 +1,186 @@ +package org.hibernate.orm.test.entitygraph; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; + +import static org.junit.jupiter.api.Assertions.assertNull; + +@Jpa( + annotatedClasses = { + FindWithEntityGraphTest.Person.class, + FindWithEntityGraphTest.PersonContact.class + } +) +@JiraKey("HHH-16960") +public class FindWithEntityGraphTest { + + + @BeforeAll + public void setUp(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + Person person = new Person( 1L, "test" ); + + Person child1 = new Person( 2L, "child1" ); + Person child2 = new Person( 2L, "child2" ); + child1.addParent( person ); + child2.addParent( person ); + entityManager.persist( person ); + } + ); + } + + @Test + public void testFind(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + EntityGraph personGraph = entityManager.createEntityGraph( Person.class ); + personGraph.addAttributeNodes( "children" ); + + Person loadedPerson = entityManager.find( + Person.class, + 1l, + Map.of( "javax.persistence.fetchgraph", personGraph ) + ); + + PersonContact personContact = loadedPerson.getPersonContact(); + assertNull( personContact ); + } + ); + } + + @Entity(name = "Person") + public static class Person { + + @Id + private Long id; + + private String name; + + @ManyToOne + private Person parent; + + @OneToOne(mappedBy = "person", orphanRemoval = true, cascade = CascadeType.ALL) + private PersonContact personContact; + + @OneToMany(mappedBy = "parent") + private Set children = new HashSet<>( 0 ); + + public Person() { + } + + public Person(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public PersonContact getPersonContact() { + return personContact; + } + + public void setPersonContact(PersonContact personContact) { + this.personContact = personContact; + } + + public Person getParent() { + return parent; + } + + public void setParent(Person parent) { + this.parent = parent; + } + + public void addParent(Person parent) { + this.parent = parent; + parent.getChildren().add( this ); + } + + public Set getChildren() { + return children; + } + + public void setChildren(Set children) { + this.children = children; + } + } + + @Entity(name = "PersonContact") + public static class PersonContact { + @Id + private Long id; + + @Column + private String address; + + public PersonContact() { + } + + public PersonContact(String address) { + this.address = address; + } + + @OneToOne(optional = false, fetch = FetchType.LAZY) + @MapsId + private Person person; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public Person getPerson() { + return person; + } + + public void setPerson(Person person) { + this.person = person; + } + } + + +} From 6c690f561c002a66e473274b98b82573288c2af3 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 22 Jan 2024 19:35:20 +0100 Subject: [PATCH 064/321] HHH-16960 OneToOne lazy loading fails when fetch graph is involved --- .../metamodel/mapping/internal/ToOneAttributeMapping.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java index 824e1c875653..a30f447436d9 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java @@ -1630,9 +1630,12 @@ else if ( hasNotFoundAction() final boolean selectByUniqueKey = isSelectByUniqueKey( side ); // Consider all associations annotated with @NotFound as EAGER + // and LAZY one-to-one that are not instrumented and not optional if ( fetchTiming == FetchTiming.IMMEDIATE || hasNotFoundAction() - || getAssociatedEntityMappingType().getSoftDeleteMapping() != null ) { + || getAssociatedEntityMappingType().getSoftDeleteMapping() != null + || ( !entityMappingType.getEntityPersister().isInstrumented() + && cardinality == Cardinality.ONE_TO_ONE && isOptional ) ) { return buildEntityFetchSelect( fetchParent, this, From e0ea066d3fdf99bcef9b1cdc1c26869eb2863d4e Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 24 Jan 2024 14:35:35 +0100 Subject: [PATCH 065/321] HHH-17674 Add test for issue --- .../orm/test/eviction/EvictAndGetTest.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/eviction/EvictAndGetTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/eviction/EvictAndGetTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/eviction/EvictAndGetTest.java new file mode 100644 index 000000000000..febefbc95ece --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/eviction/EvictAndGetTest.java @@ -0,0 +1,50 @@ +package org.hibernate.orm.test.eviction; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +@DomainModel( + annotatedClasses = EvictAndGetTest.TestEntity.class +) +@SessionFactory +public class EvictAndGetTest { + + @Test + public void testGetAfterEviction(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + TestEntity proxy = session.getReference( TestEntity.class, 1L ); + + TestEntity entity = new TestEntity( 1L ); + session.persist( entity ); + + session.flush(); + + session.evict( entity ); + + session.get( TestEntity.class, 1L ); + } + ); + } + + @Entity(name = "TestEntity") + public static class TestEntity { + + @Id + private Long id; + + private String name; + + public TestEntity() { + } + + public TestEntity(Long id) { + this.id = id; + } + } +} From f2a699bafaf7d11c9ccbdbc21f601beb66196bf6 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 24 Jan 2024 14:36:30 +0100 Subject: [PATCH 066/321] HHH-17674 NullPointerException thrown when loading entity previously evicted and proxied --- .../hibernate/engine/internal/StatefulPersistenceContext.java | 1 + 1 file changed, 1 insertion(+) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index ca96d0d8910d..f3e9bd8ea9fb 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -515,6 +515,7 @@ public Object removeEntity(EntityKey key) { if ( holder != null ) { final Object entity = holder.entity; if ( holder.proxy != null ) { + holder.entity = null; entitiesByKey.put( key, holder ); } return entity; From 3b0ec3216245bfc5cf7445b8672942b99b262535 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Mon, 15 Jan 2024 11:38:32 +0100 Subject: [PATCH 067/321] HHH-17629 Add test for issue --- .../entitygraph/EntityGraphAndJoinTest.java | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/EntityGraphAndJoinTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/EntityGraphAndJoinTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/EntityGraphAndJoinTest.java new file mode 100644 index 000000000000..74f8c51c9337 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/EntityGraphAndJoinTest.java @@ -0,0 +1,160 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.entitygraph; + +import java.util.List; + +import org.hibernate.testing.jdbc.SQLStatementInspector; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.NamedAttributeNode; +import jakarta.persistence.NamedEntityGraph; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Join; +import jakarta.persistence.criteria.JoinType; +import jakarta.persistence.criteria.Root; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.jpa.SpecHints.HINT_SPEC_FETCH_GRAPH; + +/** + * @author Marco Belladelli + */ +@DomainModel( annotatedClasses = { + EntityGraphAndJoinTest.Person.class, + EntityGraphAndJoinTest.Address.class, +} ) +@SessionFactory( useCollectingStatementInspector = true ) +@Jira( "https://hibernate.atlassian.net/browse/HHH-17629" ) +public class EntityGraphAndJoinTest { + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final Address a1 = new Address( 1L, "test" ); + final Address a2 = new Address( 2L, "test" ); + session.persist( a1 ); + session.persist( a2 ); + session.persist( new Person( "Marco", a1 ) ); + session.persist( new Person( "Andrea", a2 ) ); + } ); + } + + @Test + public void testHqlJoin(SessionFactoryScope scope) { + executeQuery( scope, false, false ); + } + + @Test + public void testHqlLeftJoin(SessionFactoryScope scope) { + executeQuery( scope, false, true ); + } + + @Test + public void testCriteriaJoin(SessionFactoryScope scope) { + executeQuery( scope, true, false ); + } + + @Test + public void testCriteriaLeftJoin(SessionFactoryScope scope) { + executeQuery( scope, true, true ); + } + + private void executeQuery(SessionFactoryScope scope, boolean criteria, boolean leftJoin) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + inspector.clear(); + + scope.inTransaction( session -> { + final TypedQuery query; + if ( criteria ) { + final CriteriaBuilder cb = session.getCriteriaBuilder(); + final CriteriaQuery cq = cb.createQuery( Person.class ); + final Root root = cq.from( Person.class ); + final Join join = root.join( "address", leftJoin ? JoinType.LEFT : JoinType.INNER ); + cq.distinct( true ).where( cb.equal( join.get( "description" ), "test" ) ).orderBy( cb.asc( join.get( "id" ) ) ); + query = session.createQuery( cq.distinct( true ) ); + } + else { + query = session.createQuery( String.format( + "select distinct p from Person p %s p.address a where a.description = 'test' order by a.id", + leftJoin ? "left join" : "join" + ), Person.class ); + } + final EntityGraph entityGraph = session.getEntityGraph( "test-graph" ); + final List resultList = query.setHint( HINT_SPEC_FETCH_GRAPH, entityGraph ).getResultList(); + assertThat( resultList ).hasSize( 2 ); + assertThat( resultList.stream().map( p -> p.getAddress().getId() ) ).containsExactly( 1L, 2L ); + inspector.assertExecutedCount( 1 ); + inspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 ); + } ); + } + + @Entity( name = "Address" ) + public static class Address { + @Id + private Long id; + + private String description; + + public Address() { + } + + public Address(Long id, String description) { + this.id = id; + this.description = description; + } + + public Long getId() { + return id; + } + + public String getDescription() { + return description; + } + } + + @Entity( name = "Person" ) + @NamedEntityGraph( name = "test-graph", attributeNodes = { + @NamedAttributeNode( "address" ), + } ) + public static class Person { + @Id + @GeneratedValue + private Integer id; + + private String name; + + @ManyToOne( fetch = FetchType.LAZY ) + @JoinColumn( name = "address_id" ) + private Address address; + + public Person() { + } + + public Person(String name, Address address) { + this.name = name; + this.address = address; + } + + public Address getAddress() { + return address; + } + } +} From e8b5a23dd2e44e254ed6a7df6122b9cdf1e72a3b Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Mon, 15 Jan 2024 11:39:26 +0100 Subject: [PATCH 068/321] HHH-17629 Reuse compatible joins for entity graphs and fetch profiles --- .../sqm/sql/BaseSqmToSqlAstConverter.java | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index e3d686786df3..bf44cfd73de9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -8148,21 +8148,33 @@ else if ( getLoadQueryInfluencers().hasEnabledFetchProfiles() ) { fetchablePath, np -> { // generate the join + final TableGroup tableGroup; final TableGroup lhs = fromClauseIndex.getTableGroup( fetchParent.getNavigablePath() ); - final TableGroupJoin tableGroupJoin = ( (TableGroupJoinProducer) fetchable ).createTableGroupJoin( - fetchablePath, - lhs, - alias, - null, - null, - true, - false, - BaseSqmToSqlAstConverter.this + final TableGroupJoinProducer joinProducer = (TableGroupJoinProducer) fetchable; + final TableGroup compatibleTableGroup = lhs.findCompatibleJoinedGroup( + joinProducer, + joinProducer.determineSqlJoinType( lhs, null, true ) ); - lhs.addTableGroupJoin( tableGroupJoin ); + if ( compatibleTableGroup == null ) { + final TableGroupJoin tableGroupJoin = joinProducer.createTableGroupJoin( + fetchablePath, + lhs, + alias, + null, + null, + true, + false, + BaseSqmToSqlAstConverter.this + ); + lhs.addTableGroupJoin( tableGroupJoin ); + tableGroup = tableGroupJoin.getJoinedGroup(); + } + else { + tableGroup = compatibleTableGroup; + } // and return the joined group - return tableGroupJoin.getJoinedGroup(); + return tableGroup; } ); } From b97cb3719d3d8b93bab2cdef81692b744d86b7d6 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 24 Jan 2024 15:39:25 +0100 Subject: [PATCH 069/321] HHH-17113 Add test for issue --- ...inedInheritanceForceDiscriminatorTest.java | 207 ++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/JoinedInheritanceForceDiscriminatorTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/JoinedInheritanceForceDiscriminatorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/JoinedInheritanceForceDiscriminatorTest.java new file mode 100644 index 000000000000..a493068030e4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/JoinedInheritanceForceDiscriminatorTest.java @@ -0,0 +1,207 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.inheritance.discriminator; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.Hibernate; +import org.hibernate.annotations.DiscriminatorOptions; + +import org.hibernate.testing.jdbc.SQLStatementInspector; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Andrea Boriero + * @author Marco Belladelli + */ +@DomainModel( annotatedClasses = { + JoinedInheritanceForceDiscriminatorTest.CommonBase.class, + JoinedInheritanceForceDiscriminatorTest.ElementEntity.class, + JoinedInheritanceForceDiscriminatorTest.AnotherEntity.class, + JoinedInheritanceForceDiscriminatorTest.ElementGroup.class +} ) +@SessionFactory( useCollectingStatementInspector = true ) +@Jira( "https://hibernate.atlassian.net/browse/HHH-17113" ) +public class JoinedInheritanceForceDiscriminatorTest { + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final ElementEntity element = new ElementEntity( 1L, "element_1" ); + session.persist( element ); + final AnotherEntity another = new AnotherEntity( 2L, "another_2" ); + session.persist( another ); + final ElementGroup elementGroup = new ElementGroup( 3L ); + elementGroup.addElement( element ); + session.persist( elementGroup ); + } ); + scope.inTransaction( session -> { + // Emulate association with AnotherEntity on the same element_table + session.createNativeMutationQuery( "update element_table set group_id = 3 where id = 2" ).executeUpdate(); + } ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.createMutationQuery( "delete from CommonBase" ).executeUpdate(); + session.createMutationQuery( "delete from ElementGroup" ).executeUpdate(); + } ); + } + + @Test + public void testFindAndLoad(SessionFactoryScope scope) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + scope.inTransaction( session -> { + final ElementGroup group = session.find( ElementGroup.class, 3L ); + inspector.clear(); + final List elements = group.getElements(); + assertThat( Hibernate.isInitialized( elements ) ).isFalse(); + assertThat( elements ).hasSize( 1 ); + inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "disc_col", 1 ); + final CommonBase commonBase = elements.get( 0 ); + assertThat( commonBase.getId() ).isEqualTo( 1L ); + assertThat( commonBase.getName() ).isEqualTo( "element_1" ); + } ); + } + + @Test + public void testQueryAndLoad(SessionFactoryScope scope) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + scope.inTransaction( session -> { + final ElementGroup group = session.createQuery( + "from ElementGroup", + ElementGroup.class + ).getSingleResult(); + inspector.clear(); + final List elements = group.getElements(); + assertThat( Hibernate.isInitialized( elements ) ).isFalse(); + assertThat( elements ).hasSize( 1 ); + inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "disc_col", 1 ); + final CommonBase commonBase = elements.get( 0 ); + assertThat( commonBase.getId() ).isEqualTo( 1L ); + assertThat( commonBase.getName() ).isEqualTo( "element_1" ); + } ); + } + + @Test + public void testQueryAndJoinFetch(SessionFactoryScope scope) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + scope.inTransaction( session -> { + final ElementGroup group = session.createQuery( + "from ElementGroup g join fetch g.elements", + ElementGroup.class + ).getSingleResult(); + inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "disc_col", 1 ); + final List elements = group.getElements(); + assertThat( Hibernate.isInitialized( elements ) ).isTrue(); + assertThat( elements ).hasSize( 1 ); + final CommonBase commonBase = elements.get( 0 ); + assertThat( commonBase.getId() ).isEqualTo( 1L ); + assertThat( commonBase.getName() ).isEqualTo( "element_1" ); + } ); + } + + @Inheritance( strategy = InheritanceType.JOINED ) + @DiscriminatorColumn( name = "disc_col" ) + @DiscriminatorOptions( force = true ) + @Entity( name = "CommonBase" ) + public static class CommonBase { + @Id + protected Long id; + + protected String name; + + public CommonBase() { + } + + public CommonBase(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + } + + @Entity( name = "ElementEntity" ) + @DiscriminatorValue( "element" ) + @Table( name = "element_table" ) + public static class ElementEntity extends CommonBase { + public ElementEntity() { + } + + public ElementEntity(Long id, String name) { + super( id, name ); + } + } + + @Entity( name = "AnotherEntity" ) + @DiscriminatorValue( "another" ) + @Table( name = "element_table" ) + public static class AnotherEntity extends CommonBase { + public AnotherEntity() { + } + + public AnotherEntity(Long id, String name) { + super( id, name ); + } + } + + @Entity( name = "ElementGroup" ) + public static class ElementGroup { + @Id + protected Long id; + + @OneToMany( fetch = FetchType.LAZY ) + @JoinColumn( name = "group_id" ) + private List elements = new ArrayList<>(); + + public ElementGroup() { + } + + public ElementGroup(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public List getElements() { + return elements; + } + + public void addElement(ElementEntity element) { + elements.add( element ); + } + } +} \ No newline at end of file From bfa1a86fea2c3dc3866e584f2afe4091741e0534 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 24 Jan 2024 16:27:01 +0100 Subject: [PATCH 070/321] HHH-17113 Fix joined inheritance and force discriminator pruning --- .../persister/entity/AbstractEntityPersister.java | 13 +++++++------ .../entity/JoinedSubclassEntityPersister.java | 8 +++++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 428429cefb7e..945d395cbd6e 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -3137,20 +3137,21 @@ public void applyDiscriminator( assert !creationState.supportsEntityNameUsage() : "Entity name usage should have been used instead"; final Map entityNameUseMap; final Collection subMappingTypes = getSubMappingTypes(); + entityNameUseMap = new HashMap<>( 1 + subMappingTypes.size() + ( isInherited() ? 1 : 0 ) ); if ( subMappingTypes.isEmpty() ) { - entityNameUseMap = Collections.singletonMap( getEntityName(), EntityNameUse.TREAT ); + entityNameUseMap.put( getEntityName(), EntityNameUse.TREAT ); } else { - entityNameUseMap = new HashMap<>( 1 + subMappingTypes.size() ); entityNameUseMap.put( getEntityName(), EntityNameUse.TREAT ); // We need to register TREAT uses for all subtypes when pruning for ( EntityMappingType subMappingType : subMappingTypes ) { entityNameUseMap.put( subMappingType.getEntityName(), EntityNameUse.TREAT ); } - if ( isInherited() ) { - // Make sure the table group includes the root table when needed for TREAT - tableGroup.resolveTableReference( getRootTableName() ); - } + } + if ( isInherited() ) { + // Make sure the table group includes the root table when needed for TREAT + tableGroup.resolveTableReference( getRootTableName() ); + entityNameUseMap.put( getRootEntityName(), EntityNameUse.EXPRESSION ); } pruneForSubclasses( tableGroup, entityNameUseMap ); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java index 4d4e918f8c8e..7fd51e550f78 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java @@ -1379,8 +1379,8 @@ public void pruneForSubclasses(TableGroup tableGroup, Map // We allow multiple joined subclasses to use the same table if they define a discriminator column. // In this case, we might need to add a discriminator condition to make sure we filter the correct subtype, // see SingleTableEntityPersister#pruneForSubclasses for more details on this condition - needsTreatDiscriminator = needsTreatDiscriminator || !persister.isAbstract() && - !isTypeOrSuperType( persister ) && useKind == EntityNameUse.UseKind.TREAT; + needsTreatDiscriminator = needsTreatDiscriminator || !persister.isAbstract() + && useKind == EntityNameUse.UseKind.TREAT && ( isInherited() || !isTypeOrSuperType( persister ) ); } } // If no tables to inner join have been found, we add at least the super class tables of this persister @@ -1411,11 +1411,13 @@ public void pruneForSubclasses(TableGroup tableGroup, Map entityNameUses, metamodel ); - for ( int i = 0; !applied && i < tableReferenceJoins.size(); i++ ) { + int i = 0; + for ( ; !applied && i < tableReferenceJoins.size(); i++ ) { final TableReferenceJoin join = tableReferenceJoins.get( i ); applied = applyDiscriminatorPredicate( join, join.getJoinedTableReference(), entityNameUses, metamodel ); } assert applied : "Could not apply treat discriminator predicate to root table join"; + assert i == 0 || retainedTableReferences.contains( tableReferenceJoins.get( i - 1 ).getJoinedTableReference() ); } } if ( tableReferenceJoins.isEmpty() ) { From 653110939e3eda5d9e74ae2d2eaa2823185c113e Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 24 Jan 2024 11:18:03 +0100 Subject: [PATCH 071/321] HHH-17668 Add test for issue --- .../refresh/MergeAndRefreshTest.java | 118 +++++++++ ...LockRefreshReferencedAndCascadingTest.java | 242 ++++++++++++++++++ 2 files changed, 360 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/refresh/MergeAndRefreshTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockRefreshReferencedAndCascadingTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/refresh/MergeAndRefreshTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/refresh/MergeAndRefreshTest.java new file mode 100644 index 000000000000..32b5a3692cfd --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/refresh/MergeAndRefreshTest.java @@ -0,0 +1,118 @@ +package org.hibernate.orm.test.bytecode.enhancement.refresh; + +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; + +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.orm.junit.JiraKey; +import org.junit.Test; +import org.junit.runner.RunWith; + +import jakarta.persistence.Cacheable; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; + +@RunWith(BytecodeEnhancerRunner.class) +@JiraKey("HHH-17668") +public class MergeAndRefreshTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Phase.class, + PhaseDescription.class + }; + } + + @Test + public void testRefresh() { + Long phaseId = 1L; + inTransaction( + session -> { + PhaseDescription description = new PhaseDescription("phase 1"); + Phase phase = new Phase( phaseId, description ); + session.persist( phase ); + } + ); + + Phase phase = fromTransaction( + session -> { + return session.find( Phase.class, phaseId ); + } + ); + + inTransaction( + session -> { + Phase merged = session.merge( phase ); + session.refresh( merged ); + } + ); + } + + @Entity(name = "Phase") + @Cacheable + @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) + public static class Phase { + @Id + private Long id; + + @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @JoinColumn(name = "phase_description_id") + private PhaseDescription description; + + private String name; + + public Phase() { + } + + public Phase(Long id, PhaseDescription description) { + this.id = id; + this.description = description; + this.description.phase = this; + } + + public Long getId() { + return id; + } + + public PhaseDescription getDescription() { + return description; + } + } + + @Entity(name = "PhaseDescription") + public static class PhaseDescription { + @Id + @GeneratedValue + private Long id; + + private String name; + + public PhaseDescription() { + } + + public PhaseDescription(String name) { + this.name = name; + } + + @OneToOne(mappedBy = "description") + @Fetch(value = FetchMode.SELECT) + private Phase phase; + + public Long getId() { + return id; + } + + public Phase getPhase() { + return phase; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockRefreshReferencedAndCascadingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockRefreshReferencedAndCascadingTest.java new file mode 100644 index 000000000000..2dd59f56a60c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockRefreshReferencedAndCascadingTest.java @@ -0,0 +1,242 @@ +package org.hibernate.orm.test.locking; + +import org.hibernate.Hibernate; + +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.LockModeType; +import jakarta.persistence.OneToOne; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Jpa( + annotatedClasses = { + LockRefreshReferencedAndCascadingTest.MainEntity.class, + LockRefreshReferencedAndCascadingTest.ReferencedEntity.class, + LockRefreshReferencedAndCascadingTest.AnotherReferencedEntity.class, + } +) +public class LockRefreshReferencedAndCascadingTest { + + @BeforeAll + public void setUp(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + final AnotherReferencedEntity anotherReferencedEntity = new AnotherReferencedEntity( + 1L, + "another lazy" + ); + final ReferencedEntity e1 = new ReferencedEntity( 0L, "lazy", anotherReferencedEntity ); + final ReferencedEntity e2 = new ReferencedEntity( 1L, "eager", null ); + entityManager.persist( e1 ); + entityManager.persist( e2 ); + final MainEntity e3 = new MainEntity( 0L, e1, e2 ); + entityManager.persist( e3 ); + } + ); + } + + @Test + public void testRefreshBeforeRead(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + MainEntity m = entityManager.find( MainEntity.class, 0L ); + assertNotNull( m ); + ReferencedEntity lazyReference = m.referencedLazy(); + ReferencedEntity eagerReference = m.referencedEager(); + assertNotNull( lazyReference ); + assertNotNull( eagerReference ); + assertFalse( Hibernate.isInitialized( lazyReference ) ); + + // First refresh, then access + entityManager.refresh( eagerReference, LockModeType.PESSIMISTIC_WRITE ); + assertFalse( Hibernate.isInitialized( lazyReference ) ); + + entityManager.refresh( lazyReference, LockModeType.PESSIMISTIC_WRITE ); + assertTrue( Hibernate.isInitialized( lazyReference ) ); + assertTrue( Hibernate.isInitialized( lazyReference.anotherReferencedEntity ) ); + + assertEquals( "lazy", lazyReference.status() ); + assertEquals( "eager", eagerReference.status() ); + assertEquals( LockModeType.PESSIMISTIC_WRITE, entityManager.getLockMode( lazyReference ) ); + assertEquals( LockModeType.PESSIMISTIC_WRITE, entityManager.getLockMode( lazyReference.getAnotherReferencedEntity() ) ); + assertEquals( LockModeType.PESSIMISTIC_WRITE, entityManager.getLockMode( eagerReference ) ); + } ); + } + + @Test + public void testRefresh(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + MainEntity m = entityManager.find( MainEntity.class, 0L ); + assertNotNull( m ); + ReferencedEntity lazyReference = m.referencedLazy(); + ReferencedEntity eagerReference = m.referencedEager(); + assertNotNull( lazyReference ); + assertNotNull( eagerReference ); + assertFalse( Hibernate.isInitialized( lazyReference ) ); + + entityManager.refresh( m ); + // CascadeType.REFRESH will trigger the initialization + assertTrue( Hibernate.isInitialized( lazyReference ) ); + + } ); + } + + @Test + public void testRefreshAfterRead(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + MainEntity m = entityManager.find( MainEntity.class, 0L ); + assertNotNull( m ); + ReferencedEntity lazyReference = m.referencedLazy(); + ReferencedEntity eagerReference = m.referencedEager(); + assertNotNull( lazyReference ); + assertNotNull( eagerReference ); + assertFalse( Hibernate.isInitialized( lazyReference ) ); + + // First access, the refresh + assertEquals( "lazy", lazyReference.status() ); + assertEquals( "eager", eagerReference.status() ); + + entityManager.refresh( lazyReference, LockModeType.PESSIMISTIC_WRITE ); + entityManager.refresh( eagerReference, LockModeType.PESSIMISTIC_WRITE ); + + assertEquals( LockModeType.PESSIMISTIC_WRITE, entityManager.getLockMode( lazyReference ) ); + assertEquals( LockModeType.PESSIMISTIC_WRITE, entityManager.getLockMode( eagerReference ) ); + } ); + } + + @Test + public void testRefreshLockMode(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + MainEntity m = entityManager.find( MainEntity.class, 0L ); + assertNotNull( m ); + ReferencedEntity lazyReference = m.referencedLazy(); + ReferencedEntity eagerReference = m.referencedEager(); + assertNotNull( lazyReference ); + assertNotNull( eagerReference ); + assertFalse( Hibernate.isInitialized( lazyReference ) ); + + entityManager.refresh( m, LockModeType.PESSIMISTIC_WRITE ); + + assertTrue( Hibernate.isInitialized( lazyReference ) ); + AnotherReferencedEntity anotherReferencedEntity = lazyReference.getAnotherReferencedEntity(); + assertTrue( Hibernate.isInitialized( anotherReferencedEntity ) ); + + assertEquals( LockModeType.PESSIMISTIC_WRITE, entityManager.getLockMode( lazyReference ) ); + assertEquals( + LockModeType.PESSIMISTIC_WRITE, + entityManager.getLockMode( anotherReferencedEntity ) + ); + } ); + } + + @Test + public void testFindWithLockMode(EntityManagerFactoryScope scope) { + scope.inTransaction( + session -> { + MainEntity mainEntity = session.find( MainEntity.class, 0L, LockModeType.PESSIMISTIC_WRITE ); + assertThat( session.getLockMode( mainEntity.referencedEager() ) ).isEqualTo( LockModeType.PESSIMISTIC_WRITE ); + } + ); + } + + @Entity(name = "MainEntity") + public static class MainEntity { + @Id + private Long id; + + private String name; + + @OneToOne(cascade = { CascadeType.REFRESH }, fetch = FetchType.LAZY) + @JoinColumn(name = "LAZY_COLUMN") + private ReferencedEntity referencedLazy; + + @OneToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "EAGER_COLUMN") + private ReferencedEntity referencedEager; + + protected MainEntity() { + } + + public MainEntity(Long id, ReferencedEntity lazy, ReferencedEntity eager) { + this.id = id; + this.referencedLazy = lazy; + this.referencedEager = eager; + } + + public ReferencedEntity referencedLazy() { + return referencedLazy; + } + + public ReferencedEntity referencedEager() { + return referencedEager; + } + } + + @Entity(name = "ReferencedEntity") + public static class ReferencedEntity { + + @Id + private Long id; + + private String status; + + @OneToOne(cascade = { CascadeType.PERSIST, CascadeType.REFRESH }, fetch = FetchType.LAZY) + private AnotherReferencedEntity anotherReferencedEntity; + + protected ReferencedEntity() { + } + + public ReferencedEntity(Long id, String status, AnotherReferencedEntity anotherReferencedEntity) { + this.id = id; + this.status = status; + this.anotherReferencedEntity = anotherReferencedEntity; + } + + public String status() { + return status; + } + + public AnotherReferencedEntity getAnotherReferencedEntity() { + return anotherReferencedEntity; + } + } + + @Entity(name = "AnotherReferencedEntity") + public static class AnotherReferencedEntity { + + @Id + private Long id; + + private String status; + + protected AnotherReferencedEntity() { + } + + public AnotherReferencedEntity(Long id, String status) { + this.id = id; + this.status = status; + } + + public String status() { + return status; + } + } + +} From 56deb7d89c8c77996bf555deb28954bc3cc0e1f8 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 24 Jan 2024 11:52:04 +0100 Subject: [PATCH 072/321] HHH-17668 NullPointerException when refreshing bytecode-enhanced entity from second-level cache --- .../internal/DefaultRefreshEventListener.java | 44 +++++++++++++------ .../hibernate/event/spi/RefreshContext.java | 9 ++++ 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java index f9ef7d2fbf33..b4025f31c2a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java @@ -64,21 +64,39 @@ public void onRefresh(RefreshEvent event, RefreshContext refreshedAlready) { final Object object = event.getObject(); if ( persistenceContext.reassociateIfUninitializedProxy( object ) ) { final boolean isTransient = isTransient( event, source, object ); + // If refreshAlready is not empty then the refresh is the result of a + // cascade refresh and the refresh of the parent will take care of initializing the lazy + // entity and setting the correct lock on it, this is needed only when the refresh is called directly on a lazy entity + if ( refreshedAlready.isEmpty() ) { + final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( object ); + final EntityPersister persister; + if ( lazyInitializer != null ) { + persister = source.getEntityPersister( lazyInitializer.getEntityName(), object ); + } + else if ( !isTransient ) { + final EntityEntry entry = persistenceContext.getEntry( object ); + persister = entry.getPersister(); + } + else { + persister = source.getEntityPersister( source.guessEntityName( object ), object ); + } - final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( object ); - final EntityPersister persister = source.getEntityPersister( lazyInitializer.getEntityName(), object ); - refresh( - event, - null, - source, - persister, - lazyInitializer, - null, - persister.getIdentifier( object, event.getSession() ), - persistenceContext - ); + refresh( + event, + null, + source, + persister, + lazyInitializer, + null, + persister.getIdentifier( object, event.getSession() ), + persistenceContext + ); + if ( lazyInitializer != null ) { + refreshedAlready.add( lazyInitializer.getImplementation() ); + } + } - if ( isTransient ) { + if ( isTransient ) { source.setReadOnly( object, source.isDefaultReadOnly() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/RefreshContext.java b/hibernate-core/src/main/java/org/hibernate/event/spi/RefreshContext.java index 8b311092b567..5ecd4614c122 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/RefreshContext.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/RefreshContext.java @@ -19,6 +19,10 @@ public interface RefreshContext { boolean add(Object entity); + default boolean isEmpty() { + return false; + } + static RefreshContext create() { // use extension to avoid creating // a useless wrapper object @@ -32,6 +36,11 @@ class Impl extends IdentityHashMap public boolean add(Object entity) { return put(entity,entity)==null; } + + @Override + public boolean isEmpty() { + return size() == 0; + } } return new Impl(); } From 8a39ea95c153ce030fd4b2d245ea62bdf2a65293 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Thu, 25 Jan 2024 14:19:58 +0100 Subject: [PATCH 073/321] HHH-17677 handle literal null arguments more elegantly in StandardFunctionReturnTypeResolvers resolves a very confusing error message --- .../StandardFunctionReturnTypeResolvers.java | 19 +++++++++++-------- .../results/internal/SqlSelectionImpl.java | 7 +++++-- .../orm/test/query/hql/FunctionTests.java | 18 ++++++++++++++++++ 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/StandardFunctionReturnTypeResolvers.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/StandardFunctionReturnTypeResolvers.java index af98edc2b25d..59fc361a7be5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/StandardFunctionReturnTypeResolvers.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/StandardFunctionReturnTypeResolvers.java @@ -12,16 +12,15 @@ import java.util.function.Supplier; import org.hibernate.Internal; -import org.hibernate.QueryException; import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.MappingModelExpressible; -import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.query.ReturnableType; import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.tree.SqmTypedNode; +import org.hibernate.query.sqm.tree.expression.NullSqmExpressible; import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.type.BasicType; @@ -252,7 +251,10 @@ public static ReturnableType extractArgumentType( int position) { final SqmTypedNode specifiedArgument = arguments.get( position - 1 ); final SqmExpressible specifiedArgType = getArgumentExpressible( specifiedArgument ); - if ( specifiedArgType != null && !(specifiedArgType instanceof ReturnableType ) ) { + if ( specifiedArgType == null || specifiedArgType instanceof NullSqmExpressible ) { + return null; + } + else if ( !(specifiedArgType instanceof ReturnableType) ) { throw new FunctionArgumentException( String.format( Locale.ROOT, @@ -263,8 +265,9 @@ public static ReturnableType extractArgumentType( ) ); } - - return (ReturnableType) specifiedArgType; + else { + return (ReturnableType) specifiedArgType; + } } private static SqmExpressible getArgumentExpressible(SqmTypedNode specifiedArgument) { @@ -272,9 +275,9 @@ private static SqmExpressible getArgumentExpressible(SqmTypedNode specifie final SqmExpressible specifiedArgType = expressible instanceof SqmTypedNode ? ( (SqmTypedNode) expressible ).getNodeType() : expressible; - return specifiedArgType instanceof SqmPathSource ? - ( (SqmPathSource) specifiedArgType ).getSqmPathType() : - specifiedArgType; + return specifiedArgType instanceof SqmPathSource + ? ( (SqmPathSource) specifiedArgType ).getSqmPathType() + : specifiedArgType; } public static JdbcMapping extractArgumentJdbcMapping( diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/SqlSelectionImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/SqlSelectionImpl.java index 92425cb67f4b..b407ace7530b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/SqlSelectionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/SqlSelectionImpl.java @@ -11,7 +11,6 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMappingContainer; -import org.hibernate.metamodel.mapping.SqlExpressible; import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.spi.SqlExpressionAccess; import org.hibernate.sql.ast.spi.SqlSelection; @@ -93,7 +92,10 @@ protected SqlSelectionImpl( } private static ValueExtractor determineValueExtractor(Expression sqlExpression, JavaType jdbcJavaType) { - final JdbcMapping jdbcMapping = sqlExpression.getExpressionType().getSingleJdbcMapping(); + final JdbcMappingContainer expressionType = sqlExpression.getExpressionType(); + final JdbcMapping jdbcMapping = expressionType == null + ? JavaObjectType.INSTANCE + : expressionType.getSingleJdbcMapping(); if ( jdbcJavaType == null || jdbcMapping.getMappedJavaType() == jdbcJavaType ) { return jdbcMapping.getJdbcValueExtractor(); } @@ -102,6 +104,7 @@ private static ValueExtractor determineValueExtractor(Expression sqlExpression, } } + @Override public Expression getExpression() { return sqlExpression; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java index bf9264392b11..6c3e7709270b 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java @@ -2248,4 +2248,22 @@ public void testTupleInSelect(SessionFactoryScope scope) { } ); } + + @Test + @SkipForDialect(dialectClass = H2Dialect.class) + @SkipForDialect(dialectClass = DerbyDialect.class) + @SkipForDialect(dialectClass = HSQLDialect.class) + @SkipForDialect(dialectClass = DB2Dialect.class) + public void testNullInCoalesce(SessionFactoryScope scope) { + scope.inTransaction(s -> { + assertEquals("hello", + s.createQuery("select coalesce(null, :word)", String.class) + .setParameter("word", "hello") + .getSingleResultOrNull()); + assertEquals("hello", + s.createQuery("select coalesce(:word, null)", String.class) + .setParameter("word", "hello") + .getSingleResultOrNull()); + }); + } } From 122598dcc2fdceab719b59e85f07d72e3cf5cf5c Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 26 Jan 2024 12:24:23 +0100 Subject: [PATCH 074/321] HHH-17681 Restore AbstractSqmSelfRenderingFunctionDescriptor backwards compatibility --- .../AbstractSqmSelfRenderingFunctionDescriptor.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/AbstractSqmSelfRenderingFunctionDescriptor.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/AbstractSqmSelfRenderingFunctionDescriptor.java index c999e266434d..5a6078a82924 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/AbstractSqmSelfRenderingFunctionDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/AbstractSqmSelfRenderingFunctionDescriptor.java @@ -14,6 +14,9 @@ import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.query.sqm.tree.predicate.SqmPredicate; import org.hibernate.query.sqm.tree.select.SqmOrderByClause; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; import java.util.List; @@ -44,6 +47,15 @@ public AbstractSqmSelfRenderingFunctionDescriptor( this.functionKind = functionKind; } + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + ReturnableType returnType, + SqlAstTranslator walker) { + render( sqlAppender, sqlAstArguments, walker ); + } + @Override public FunctionKind getFunctionKind() { return functionKind; From 28d8ec578fdb0c6ee53c2c3491cdb297ae903416 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Fri, 26 Jan 2024 15:31:44 +0100 Subject: [PATCH 075/321] HHH-17679 Add test for issue --- ...ManyToManyJoinTableAndInheritanceTest.java | 199 +++++++++++++++++ ...JoinedInheritanceAndDiscriminatorTest.java | 205 ++++++++++++++++++ 2 files changed, 404 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/ManyToManyJoinTableAndInheritanceTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/OneToManyJoinedInheritanceAndDiscriminatorTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/ManyToManyJoinTableAndInheritanceTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/ManyToManyJoinTableAndInheritanceTest.java new file mode 100644 index 000000000000..cc91a901d2fa --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/ManyToManyJoinTableAndInheritanceTest.java @@ -0,0 +1,199 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.inheritance; + +import java.util.HashSet; +import java.util.Set; + +import org.hibernate.Hibernate; + +import org.hibernate.testing.jdbc.SQLStatementInspector; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToMany; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Marco Belladelli + */ +@DomainModel( annotatedClasses = { + ManyToManyJoinTableAndInheritanceTest.RootEntity.class, + ManyToManyJoinTableAndInheritanceTest.ParentEntity.class, + ManyToManyJoinTableAndInheritanceTest.Sub1.class, +} ) +@SessionFactory( useCollectingStatementInspector = true ) +@Jira( "https://hibernate.atlassian.net/browse/HHH-17679" ) +public class ManyToManyJoinTableAndInheritanceTest { + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final Sub1 sub1 = new Sub1( 1L, 1, 1 ); + session.persist( sub1 ); + final RootEntity root = new RootEntity(); + root.getNodesPoly().add( sub1 ); + session.persist( root ); + } ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.createMutationQuery( "delete from RootEntity" ).executeUpdate(); + session.createMutationQuery( "delete from ParentEntity" ).executeUpdate(); + } ); + } + + @Test + public void testLeftJoinSelectParent(SessionFactoryScope scope) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + inspector.clear(); + + scope.inTransaction( session -> { + final RootEntity result = session.createQuery( + "select root from RootEntity root left join root.nodesPoly _collection", + RootEntity.class + ).getSingleResult(); + inspector.assertExecutedCount( 1 ); + inspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 ); + assertThat( Hibernate.isInitialized( result.getNodesPoly() ) ).isFalse(); + assertThat( result.getNodesPoly() ).hasSize( 1 ); + assertThat( result.getNodesPoly().iterator().next().getId() ).isEqualTo( 1L ); + } ); + } + + @Test + public void testLeftJoinSelectId(SessionFactoryScope scope) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + inspector.clear(); + + scope.inTransaction( session -> { + final Long result = session.createQuery( + "select _collection.id from RootEntity root left join root.nodesPoly _collection", + Long.class + ).getSingleResult(); + assertThat( result ).isEqualTo( 1L ); + inspector.assertExecutedCount( 1 ); + inspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 ); + } ); + } + + @Test + public void testLeftJoinSelectElement(SessionFactoryScope scope) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + inspector.clear(); + + scope.inTransaction( session -> { + final Sub1 result = session.createQuery( + "select _collection from RootEntity root left join root.nodesPoly _collection", + Sub1.class + ).getSingleResult(); + inspector.assertExecutedCount( 1 ); + inspector.assertNumberOfOccurrenceInQuery( 0, "join", 2 ); + assertThat( result.getId() ).isEqualTo( 1L ); + assertThat( result.getNumber() ).isEqualTo( 1 ); + assertThat( result.getSub1Value() ).isEqualTo( 1 ); + } ); + } + + @Test + public void testLeftJoinFetch(SessionFactoryScope scope) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + inspector.clear(); + + scope.inTransaction( session -> { + final RootEntity result = session.createQuery( + "select root from RootEntity root left join fetch root.nodesPoly _collection", + RootEntity.class + ).getSingleResult(); + inspector.assertExecutedCount( 1 ); + inspector.assertNumberOfOccurrenceInQuery( 0, "join", 2 ); + assertThat( Hibernate.isInitialized( result.getNodesPoly() ) ).isTrue(); + assertThat( result.getNodesPoly() ).hasSize( 1 ); + assertThat( result.getNodesPoly().iterator().next().getId() ).isEqualTo( 1L ); + } ); + } + + @Entity( name = "RootEntity" ) + public static class RootEntity { + @Id + @GeneratedValue + private Long id; + + @ManyToMany + @JoinTable( + name = "set_one_to_many_poly", + joinColumns = @JoinColumn( name = "root_id" ), + inverseJoinColumns = @JoinColumn( name = "poly_id" ) + ) + private Set nodesPoly = new HashSet<>(); + + public Set getNodesPoly() { + return nodesPoly; + } + } + + @Entity( name = "ParentEntity" ) + @DiscriminatorValue( "0" ) + @Inheritance( strategy = InheritanceType.SINGLE_TABLE ) + public static class ParentEntity { + @Id + private Long id; + + @Column( name = "number_col" ) + private Integer number; + + public ParentEntity() { + } + + public ParentEntity(Long id, Integer number) { + this.id = id; + this.number = number; + } + + public Long getId() { + return id; + } + + public Integer getNumber() { + return number; + } + } + + @Entity( name = "Sub1" ) + @DiscriminatorValue( "1" ) + public static class Sub1 extends ParentEntity { + private Integer sub1Value; + + public Sub1() { + } + + public Sub1(Long id, Integer number, Integer sub1Value) { + super( id, number ); + this.sub1Value = sub1Value; + } + + public Integer getSub1Value() { + return sub1Value; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/OneToManyJoinedInheritanceAndDiscriminatorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/OneToManyJoinedInheritanceAndDiscriminatorTest.java new file mode 100644 index 000000000000..e5855eeec459 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/OneToManyJoinedInheritanceAndDiscriminatorTest.java @@ -0,0 +1,205 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.inheritance; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Marco Belladelli + */ +@SessionFactory +@DomainModel( annotatedClasses = { + OneToManyJoinedInheritanceAndDiscriminatorTest.Company.class, + OneToManyJoinedInheritanceAndDiscriminatorTest.CustomerCompany.class, + OneToManyJoinedInheritanceAndDiscriminatorTest.DistributorCompany.class, + OneToManyJoinedInheritanceAndDiscriminatorTest.ComputerSystem.class, + OneToManyJoinedInheritanceAndDiscriminatorTest.CustomerComputerSystem.class, + OneToManyJoinedInheritanceAndDiscriminatorTest.DistributorComputerSystem.class, +} ) +@Jira( "https://hibernate.atlassian.net/browse/HHH-17483" ) +public class OneToManyJoinedInheritanceAndDiscriminatorTest { + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final CustomerComputerSystem customerComputer = new CustomerComputerSystem(); + customerComputer.setId( 1L ); + session.persist( customerComputer ); + final CustomerCompany customerCompany = new CustomerCompany( 2L ); + customerCompany.addComputerSystem( customerComputer ); + session.persist( customerCompany ); + final DistributorComputerSystem distributorComputer = new DistributorComputerSystem(); + distributorComputer.setId( 3L ); + session.persist( distributorComputer ); + final DistributorCompany distributorCompany = new DistributorCompany( 4L ); + distributorCompany.addComputerSystem( distributorComputer ); + session.persist( distributorCompany ); + } ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.createMutationQuery( "delete from ComputerSystem" ).executeUpdate(); + session.createMutationQuery( "delete from Company" ).executeUpdate(); + } ); + } + + @Test + public void testQuery(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final CustomerCompany result = session.createQuery( + "from CustomerCompany", + CustomerCompany.class + ).getSingleResult(); + assertThat( result.getComputerSystems() ).hasSize( 1 ); + } ); + } + + @Test + public void testFind(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final DistributorCompany result = session.find( DistributorCompany.class, 4L ); + assertThat( result.getComputerSystems() ).hasSize( 1 ); + } ); + } + + @Test + public void testJoinSelectId(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final Long result = session.createQuery( + "select s.id from CustomerCompany c join c.computerSystems s", + Long.class + ).getSingleResult(); + assertThat( result ).isEqualTo( 1L ); + } ); + } + + @Test + public void testJoinSelectEntity(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final DistributorComputerSystem result = session.createQuery( + "select s from DistributorCompany c join c.computerSystems s", + DistributorComputerSystem.class + ).getSingleResult(); + assertThat( result.getId() ).isEqualTo( 3L ); + } ); + } + + @Entity( name = "Company" ) + @Inheritance( strategy = InheritanceType.JOINED ) + public static abstract class Company { + @Id + private Long id; + + public Company() { + } + + public Company(Long id) { + this.id = id; + } + } + + @Entity( name = "CustomerCompany" ) + public static class CustomerCompany extends Company { + @OneToMany( mappedBy = "owner" ) + private List computerSystems = new ArrayList<>(); + + public CustomerCompany() { + } + + public CustomerCompany(long id) { + super( id ); + } + + public void addComputerSystem(CustomerComputerSystem computerSystem) { + computerSystems.add( computerSystem ); + computerSystem.setOwner( this ); + } + + public List getComputerSystems() { + return computerSystems; + } + } + + @Entity( name = "DistributorCompany" ) + public static class DistributorCompany extends Company { + @OneToMany( mappedBy = "owner" ) + private List computerSystems = new ArrayList<>(); + + public DistributorCompany() { + } + + public DistributorCompany(long id) { + super( id ); + } + + public void addComputerSystem(DistributorComputerSystem computerSystem) { + computerSystems.add( computerSystem ); + computerSystem.setOwner( this ); + } + + public List getComputerSystems() { + return computerSystems; + } + } + + @Entity( name = "ComputerSystem" ) + @Table( name = "computer_system" ) + @Inheritance( strategy = InheritanceType.JOINED ) + @DiscriminatorColumn( name = "disc_col" ) + public static abstract class ComputerSystem { + @Id + private Long id; + + @ManyToOne + @JoinColumn( name = "owner_id" ) + private Company owner; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public void setOwner(Company owner) { + this.owner = owner; + } + } + + @Entity( name = "CustomerComputerSystem" ) + @Table( name = "computer_system_sub" ) + public static class CustomerComputerSystem extends ComputerSystem { + } + + @Entity( name = "DistributorComputerSystem" ) + @Table( name = "computer_system_sub" ) + public static class DistributorComputerSystem extends ComputerSystem { + } +} From 7a227d702d43bc26872e5239e3c53e3e9afe7dbc Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Fri, 26 Jan 2024 15:32:43 +0100 Subject: [PATCH 076/321] HHH-17679 Fix unnecessary table reference resolution for treat --- .../persister/entity/JoinedSubclassEntityPersister.java | 5 ++++- .../query/sqm/sql/BaseSqmToSqlAstConverter.java | 9 +++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java index 7fd51e550f78..0a9b7f6a0106 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java @@ -1417,7 +1417,10 @@ public void pruneForSubclasses(TableGroup tableGroup, Map applied = applyDiscriminatorPredicate( join, join.getJoinedTableReference(), entityNameUses, metamodel ); } assert applied : "Could not apply treat discriminator predicate to root table join"; - assert i == 0 || retainedTableReferences.contains( tableReferenceJoins.get( i - 1 ).getJoinedTableReference() ); + if ( i != 0 ) { + // Always retain the root table reference join where the discriminator was applied + retainedTableReferences.add( tableReferenceJoins.get( i - 1 ).getJoinedTableReference() ); + } } } if ( tableReferenceJoins.isEmpty() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index bf44cfd73de9..6e4dbeb676f6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -3089,7 +3089,6 @@ private void registerEntityNameUsage( actualTableGroup.resolveTableReference( null, persister.getTableName() ); } - final EntityNameUse.UseKind useKind = finalEntityNameUse.getKind(); if ( projection ) { EntityMappingType superMappingType = persister; while ( ( superMappingType = superMappingType.getSuperMappingType() ) != null ) { @@ -3100,8 +3099,10 @@ private void registerEntityNameUsage( ); } } + // If we encounter a treat or projection use, we also want register the use for all subtypes. // We do this here to not have to expand entity name uses during pruning later on + final EntityNameUse.UseKind useKind = finalEntityNameUse.getKind(); if ( useKind == EntityNameUse.UseKind.TREAT ) { for ( EntityMappingType subType : persister.getSubMappingTypes() ) { entityNameUses.compute( @@ -3109,10 +3110,6 @@ private void registerEntityNameUsage( (s, existingUse) -> finalEntityNameUse.stronger( existingUse ) ); } - if ( persister.isInherited() && persister.needsDiscriminator() ) { - // Make sure the table group includes the root table when needed for TREAT - actualTableGroup.resolveTableReference( persister.getRootTableName() ); - } } else if ( useKind == EntityNameUse.UseKind.PROJECTION ) { for ( EntityMappingType subType : persister.getSubMappingTypes() ) { @@ -3358,7 +3355,7 @@ private TableGroup consumeAttributeJoin( // This is a non-treated join with an entity which is an inheritance subtype, // register a TREAT entity name use to filter only the entities of the correct type. registerEntityNameUsage( - getActualTableGroup( joinedTableGroup, sqmJoin ), + elementTableGroup, EntityNameUse.TREAT, entityDomainType.getHibernateEntityName() ); From 88370906a43d3dac08ad1ad1880b8ea7bb410730 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 05:48:35 +0000 Subject: [PATCH 077/321] Bump com.gradle.enterprise from 3.16.1 to 3.16.2 Bumps com.gradle.enterprise from 3.16.1 to 3.16.2. --- updated-dependencies: - dependency-name: com.gradle.enterprise dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index a2ea73e80bfa..d4510618b1cc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -19,7 +19,7 @@ pluginManagement { plugins { id 'org.hibernate.orm.build.env-settings' - id 'com.gradle.enterprise' version '3.16.1' + id 'com.gradle.enterprise' version '3.16.2' id 'com.gradle.common-custom-user-data-gradle-plugin' version '1.12.1' } From b1319ad5499971de17bbcd3ef151227d98fc73f9 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 29 Jan 2024 20:20:48 +0100 Subject: [PATCH 078/321] HHH-17683 never look for session getters in mapped superclasses or embeddables --- .../jpamodelgen/annotation/AnnotationMetaEntity.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java index 0bb292d93957..eb7ceb277a09 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java @@ -302,7 +302,9 @@ else if ( containsAnnotation( method, Constants.HQL, Constants.SQL, Constants.FI } private void findSessionGetter(TypeElement type) { - if ( !containsAnnotation( type, Constants.ENTITY ) ) { + if ( !containsAnnotation( type, Constants.ENTITY ) + && !containsAnnotation( type, Constants.MAPPED_SUPERCLASS ) + && !containsAnnotation( type, Constants.EMBEDDABLE ) ) { for ( ExecutableElement method : methodsIn( type.getEnclosedElements() ) ) { if ( isSessionGetter( method ) ) { dao = true; From 18b1cea796756ab6b9342c4e3a7d9dd8ae904d70 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 23 Jan 2024 10:58:38 +0100 Subject: [PATCH 079/321] HHH-17666 Fix trunc function argument type resolver --- .../dialect/function/TruncFunction.java | 5 +- .../TruncConvertedDatetimeAttributeTest.java | 128 ++++++++++++++++++ 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/TruncConvertedDatetimeAttributeTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/TruncFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/TruncFunction.java index 837e590258d6..b1be2af649a0 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/TruncFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/TruncFunction.java @@ -72,7 +72,10 @@ public TruncFunction( "trunc", new TruncArgumentsValidator(), StandardFunctionReturnTypeResolvers.useArgType( 1 ), - StandardFunctionArgumentTypeResolvers.ARGUMENT_OR_IMPLIED_RESULT_TYPE + StandardFunctionArgumentTypeResolvers.byArgument( + StandardFunctionArgumentTypeResolvers.IMPLIED_RESULT_TYPE, + StandardFunctionArgumentTypeResolvers.NULL + ) ); this.numericRenderingSupport = new TruncRenderingSupport( new PatternRenderer( truncPattern ), diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/TruncConvertedDatetimeAttributeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/TruncConvertedDatetimeAttributeTest.java new file mode 100644 index 000000000000..ed761c1090b4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/TruncConvertedDatetimeAttributeTest.java @@ -0,0 +1,128 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.hql; + +import java.sql.Timestamp; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import org.hibernate.dialect.DerbyDialect; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.SkipForDialect; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Convert; +import jakarta.persistence.Converter; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Marco Belladelli + */ +@DomainModel( annotatedClasses = TruncConvertedDatetimeAttributeTest.TestEntity.class ) +@SessionFactory +@SkipForDialect( dialectClass = DerbyDialect.class, reason = "Derby doesn't support any form of date truncation" ) +@Jira( "https://hibernate.atlassian.net/browse/HHH-17666" ) +public class TruncConvertedDatetimeAttributeTest { + private static final Date DATE = new GregorianCalendar( 2017, Calendar.JANUARY, 24 ).getTime(); + private static final Instant INSTANT = ZonedDateTime.of( 2020, 10, 15, 20, 34, 45, 0, ZoneOffset.UTC ).toInstant(); + + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> session.persist( new TestEntity( 1L, DATE.getTime(), INSTANT ) ) ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createMutationQuery( "delete from TestEntity" ).executeUpdate() ); + } + + @Test + public void testTruncSelection(SessionFactoryScope scope) { + scope.inSession( session -> { + assertThat( session.createQuery( + "select trunc(instantCol, minute) from TestEntity", + Instant.class + ).getSingleResult() ).isEqualTo( INSTANT.truncatedTo( ChronoUnit.MINUTES ) ); + assertThat( session.createQuery( + "select trunc(dateCol, month) from TestEntity", + Long.class + ).getSingleResult() ).isEqualTo( new GregorianCalendar( 2017, Calendar.JANUARY, 1 ).getTime().getTime() ); + } ); + } + + @Test + public void testTruncComparison(SessionFactoryScope scope) { + scope.inSession( session -> { + assertThat( session.createQuery( + "from TestEntity where trunc(instantCol, hour) < current_date", + TestEntity.class + ).getResultList() ).hasSize( 1 ); + assertThat( session.createQuery( + "from TestEntity where trunc(dateCol, year) < current_timestamp", + TestEntity.class + ).getResultList() ).hasSize( 1 ); + } ); + } + + @Entity( name = "TestEntity" ) + public static class TestEntity { + @Id + private Long id; + + @Convert( converter = DateConverter.class ) + private Long dateCol; + + @Convert( converter = InstantConverter.class ) + private Instant instantCol; + + public TestEntity() { + } + + public TestEntity(Long id, Long dateCol, Instant instantCol) { + this.id = id; + this.dateCol = dateCol; + this.instantCol = instantCol; + } + } + + @Converter + public static class DateConverter implements AttributeConverter { + public Date convertToDatabaseColumn(Long time) { + return time == null ? null : new Date( time ); + } + + public Long convertToEntityAttribute(Date date) { + return date == null ? null : date.getTime(); + } + } + + @Converter + public static class InstantConverter implements AttributeConverter { + public Timestamp convertToDatabaseColumn(Instant instant) { + return instant == null ? null : Timestamp.from( instant ); + } + + public Instant convertToEntityAttribute(Timestamp timestamp) { + return timestamp == null ? null : timestamp.toInstant(); + } + } +} From c9805f21ff2986439820f84688d917ee89361bfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 29 Jan 2024 11:58:23 +0100 Subject: [PATCH 080/321] HHH-17683 Reproducer for jpamodelgen generating wrong constructor for metamodel of entities whose methods contain a `static EntityManager getEntityManager()` --- ...ingEntityWithInstanceGetEntityManager.java | 22 +++ ...ndingEntityWithStaticGetEntityManager.java | 22 +++ ...NonEntityWithInstanceGetEntityManager.java | 34 +++++ ...ngNonEntityWithStaticGetEntityManager.java | 34 +++++ ...uperClassWithInstanceGetEntityManager.java | 34 +++++ ...rSuperClassWithStaticGetEntityManager.java | 34 +++++ ...NonEntityWithInstanceGetEntityManager.java | 43 ++++++ ...ngNonEntityWithStaticGetEntityManager.java | 43 ++++++ .../EntityWithInstanceGetEntityManager.java | 44 ++++++ .../EntityWithStaticGetEntityManager.java | 41 ++++++ ...NonEntityWithInstanceGetEntityManager.java | 27 ++++ ...ngNonEntityWithStaticGetEntityManager.java | 27 ++++ ...uperClassWithInstanceGetEntityManager.java | 33 +++++ ...rSuperClassWithStaticGetEntityManager.java | 32 +++++ ...NonEntityWithInstanceGetEntityManager.java | 19 +++ .../NonEntityWithStaticGetEntityManager.java | 18 +++ .../SuperClassWithGetEntityManagerTest.java | 125 ++++++++++++++++++ .../jpamodelgen/test/util/TestUtil.java | 71 +++++++--- 18 files changed, 683 insertions(+), 20 deletions(-) create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingEntityWithInstanceGetEntityManager.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingEntityWithStaticGetEntityManager.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingMapperSuperClassExtendingNonEntityWithInstanceGetEntityManager.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingMapperSuperClassExtendingNonEntityWithStaticGetEntityManager.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingMapperSuperClassWithInstanceGetEntityManager.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingMapperSuperClassWithStaticGetEntityManager.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingNonEntityWithInstanceGetEntityManager.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingNonEntityWithStaticGetEntityManager.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityWithInstanceGetEntityManager.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityWithStaticGetEntityManager.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/MapperSuperClassExtendingNonEntityWithInstanceGetEntityManager.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/MapperSuperClassExtendingNonEntityWithStaticGetEntityManager.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/MapperSuperClassWithInstanceGetEntityManager.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/MapperSuperClassWithStaticGetEntityManager.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/NonEntityWithInstanceGetEntityManager.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/NonEntityWithStaticGetEntityManager.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/SuperClassWithGetEntityManagerTest.java diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingEntityWithInstanceGetEntityManager.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingEntityWithInstanceGetEntityManager.java new file mode 100644 index 000000000000..f67485abac2e --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingEntityWithInstanceGetEntityManager.java @@ -0,0 +1,22 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import jakarta.persistence.Entity; + +@Entity +public class EntityExtendingEntityWithInstanceGetEntityManager extends EntityWithInstanceGetEntityManager { + private String otherName; + + public String getOtherName() { + return otherName; + } + + public void setOtherName(String otherName) { + this.otherName = otherName; + } +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingEntityWithStaticGetEntityManager.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingEntityWithStaticGetEntityManager.java new file mode 100644 index 000000000000..7c741f4de78e --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingEntityWithStaticGetEntityManager.java @@ -0,0 +1,22 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import jakarta.persistence.Entity; + +@Entity +public class EntityExtendingEntityWithStaticGetEntityManager extends EntityWithStaticGetEntityManager { + private String otherName; + + public String getOtherName() { + return otherName; + } + + public void setOtherName(String otherName) { + this.otherName = otherName; + } +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingMapperSuperClassExtendingNonEntityWithInstanceGetEntityManager.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingMapperSuperClassExtendingNonEntityWithInstanceGetEntityManager.java new file mode 100644 index 000000000000..82ecf321660f --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingMapperSuperClassExtendingNonEntityWithInstanceGetEntityManager.java @@ -0,0 +1,34 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import jakarta.persistence.Entity; + +@Entity +public class EntityExtendingMapperSuperClassExtendingNonEntityWithInstanceGetEntityManager + extends MapperSuperClassExtendingNonEntityWithInstanceGetEntityManager { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + private String otherName; + + public String getOtherName() { + return otherName; + } + + public void setOtherName(String otherName) { + this.otherName = otherName; + } +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingMapperSuperClassExtendingNonEntityWithStaticGetEntityManager.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingMapperSuperClassExtendingNonEntityWithStaticGetEntityManager.java new file mode 100644 index 000000000000..d2e6476b0ac9 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingMapperSuperClassExtendingNonEntityWithStaticGetEntityManager.java @@ -0,0 +1,34 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import jakarta.persistence.Entity; + +@Entity +public class EntityExtendingMapperSuperClassExtendingNonEntityWithStaticGetEntityManager + extends MapperSuperClassExtendingNonEntityWithStaticGetEntityManager { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + private String otherName; + + public String getOtherName() { + return otherName; + } + + public void setOtherName(String otherName) { + this.otherName = otherName; + } +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingMapperSuperClassWithInstanceGetEntityManager.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingMapperSuperClassWithInstanceGetEntityManager.java new file mode 100644 index 000000000000..ba8e7f9c2d36 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingMapperSuperClassWithInstanceGetEntityManager.java @@ -0,0 +1,34 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import jakarta.persistence.Entity; + +@Entity +public class EntityExtendingMapperSuperClassWithInstanceGetEntityManager + extends MapperSuperClassWithInstanceGetEntityManager { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + private String otherName; + + public String getOtherName() { + return otherName; + } + + public void setOtherName(String otherName) { + this.otherName = otherName; + } +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingMapperSuperClassWithStaticGetEntityManager.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingMapperSuperClassWithStaticGetEntityManager.java new file mode 100644 index 000000000000..f2a726d2eecc --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingMapperSuperClassWithStaticGetEntityManager.java @@ -0,0 +1,34 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import jakarta.persistence.Entity; + +@Entity +public class EntityExtendingMapperSuperClassWithStaticGetEntityManager + extends MapperSuperClassWithStaticGetEntityManager { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + private String otherName; + + public String getOtherName() { + return otherName; + } + + public void setOtherName(String otherName) { + this.otherName = otherName; + } +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingNonEntityWithInstanceGetEntityManager.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingNonEntityWithInstanceGetEntityManager.java new file mode 100644 index 000000000000..6143e88f157a --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingNonEntityWithInstanceGetEntityManager.java @@ -0,0 +1,43 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +@Entity +public class EntityExtendingNonEntityWithInstanceGetEntityManager extends NonEntityWithInstanceGetEntityManager { + @Id + private Long id; + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + private String otherName; + + public String getOtherName() { + return otherName; + } + + public void setOtherName(String otherName) { + this.otherName = otherName; + } +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingNonEntityWithStaticGetEntityManager.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingNonEntityWithStaticGetEntityManager.java new file mode 100644 index 000000000000..daf38e452fbe --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityExtendingNonEntityWithStaticGetEntityManager.java @@ -0,0 +1,43 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +@Entity +public class EntityExtendingNonEntityWithStaticGetEntityManager extends NonEntityWithStaticGetEntityManager { + @Id + private Long id; + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + private String otherName; + + public String getOtherName() { + return otherName; + } + + public void setOtherName(String otherName) { + this.otherName = otherName; + } +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityWithInstanceGetEntityManager.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityWithInstanceGetEntityManager.java new file mode 100644 index 000000000000..9667d7faa873 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityWithInstanceGetEntityManager.java @@ -0,0 +1,44 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import jakarta.persistence.Entity; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Id; +import jakarta.persistence.Transient; + +@Entity +public class EntityWithInstanceGetEntityManager { + + @Transient + public EntityManager getEntityManager() { + // In a real-world scenario, this would contain some framework-specific code + throw new IllegalStateException( "This method shouldn't be called in tests" ); + } + + @Id + private Long id; + private String name; + private String entityManager; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityWithStaticGetEntityManager.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityWithStaticGetEntityManager.java new file mode 100644 index 000000000000..06ab0e86dd2b --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/EntityWithStaticGetEntityManager.java @@ -0,0 +1,41 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import jakarta.persistence.Entity; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Id; + +@Entity +public class EntityWithStaticGetEntityManager { + + public static EntityManager getEntityManager() { + // In a real-world scenario, this would contain some framework-specific code + throw new IllegalStateException( "This method shouldn't be called in tests" ); + } + + @Id + private Long id; + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/MapperSuperClassExtendingNonEntityWithInstanceGetEntityManager.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/MapperSuperClassExtendingNonEntityWithInstanceGetEntityManager.java new file mode 100644 index 000000000000..bc5f585451ab --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/MapperSuperClassExtendingNonEntityWithInstanceGetEntityManager.java @@ -0,0 +1,27 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; + +@MappedSuperclass +public abstract class MapperSuperClassExtendingNonEntityWithInstanceGetEntityManager + extends NonEntityWithInstanceGetEntityManager { + + @Id + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/MapperSuperClassExtendingNonEntityWithStaticGetEntityManager.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/MapperSuperClassExtendingNonEntityWithStaticGetEntityManager.java new file mode 100644 index 000000000000..81710ed28cc5 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/MapperSuperClassExtendingNonEntityWithStaticGetEntityManager.java @@ -0,0 +1,27 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; + +@MappedSuperclass +public abstract class MapperSuperClassExtendingNonEntityWithStaticGetEntityManager + extends NonEntityWithStaticGetEntityManager { + + @Id + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/MapperSuperClassWithInstanceGetEntityManager.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/MapperSuperClassWithInstanceGetEntityManager.java new file mode 100644 index 000000000000..c934fba7f08f --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/MapperSuperClassWithInstanceGetEntityManager.java @@ -0,0 +1,33 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.Transient; + +@MappedSuperclass +public abstract class MapperSuperClassWithInstanceGetEntityManager { + + @Id + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + @Transient + public String getEntityManager() { + // In a real-world scenario, this would contain some framework-specific code + throw new IllegalStateException( "This method shouldn't be called in tests" ); + } + +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/MapperSuperClassWithStaticGetEntityManager.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/MapperSuperClassWithStaticGetEntityManager.java new file mode 100644 index 000000000000..4e7b837355a0 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/MapperSuperClassWithStaticGetEntityManager.java @@ -0,0 +1,32 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; + +@MappedSuperclass +public abstract class MapperSuperClassWithStaticGetEntityManager { + + public static EntityManager getEntityManager() { + // In a real-world scenario, this would contain some framework-specific code + throw new IllegalStateException( "This method shouldn't be called in tests" ); + } + + @Id + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/NonEntityWithInstanceGetEntityManager.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/NonEntityWithInstanceGetEntityManager.java new file mode 100644 index 000000000000..7385e0048879 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/NonEntityWithInstanceGetEntityManager.java @@ -0,0 +1,19 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import jakarta.persistence.Transient; + +public abstract class NonEntityWithInstanceGetEntityManager { + + @Transient + public String getEntityManager() { + // In a real-world scenario, this would contain some framework-specific code + throw new IllegalStateException( "This method shouldn't be called in tests" ); + } + +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/NonEntityWithStaticGetEntityManager.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/NonEntityWithStaticGetEntityManager.java new file mode 100644 index 000000000000..72c88e7dac49 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/NonEntityWithStaticGetEntityManager.java @@ -0,0 +1,18 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import jakarta.persistence.EntityManager; + +public abstract class NonEntityWithStaticGetEntityManager { + + public static EntityManager getEntityManager() { + // In a real-world scenario, this would contain some framework-specific code + throw new IllegalStateException( "This method shouldn't be called in tests" ); + } + +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/SuperClassWithGetEntityManagerTest.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/SuperClassWithGetEntityManagerTest.java new file mode 100644 index 000000000000..1c1a49089427 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/constructor/SuperClassWithGetEntityManagerTest.java @@ -0,0 +1,125 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.jpamodelgen.test.constructor; + +import static org.hibernate.jpamodelgen.test.util.TestUtil.assertAbsenceOfNonDefaultConstructorInMetamodelFor; +import static org.hibernate.jpamodelgen.test.util.TestUtil.assertMetamodelClassGeneratedFor; + +import org.hibernate.jpamodelgen.test.util.CompilationTest; +import org.hibernate.jpamodelgen.test.util.TestForIssue; +import org.hibernate.jpamodelgen.test.util.TestUtil; +import org.hibernate.jpamodelgen.test.util.WithClasses; + +import org.junit.Test; + +/** + * Test various scenarios where a superclass of an entity has a {@code getEntityManager()} method. + *

+ * The superclass may be itself an entity, or a mapped superclass, or not mapped at all. + *

+ * The method may be static or not. + */ +@TestForIssue(jiraKey = "HHH-17683") +public class SuperClassWithGetEntityManagerTest extends CompilationTest { + @Test + @WithClasses({ EntityWithInstanceGetEntityManager.class, EntityExtendingEntityWithInstanceGetEntityManager.class }) + public void entityWithInstanceGetEntityManager() { + doTest( EntityWithInstanceGetEntityManager.class, EntityExtendingEntityWithInstanceGetEntityManager.class ); + } + + @Test + @WithClasses({ EntityWithStaticGetEntityManager.class, EntityExtendingEntityWithStaticGetEntityManager.class }) + public void entityWithStaticGetEntityManager() { + doTest( EntityWithStaticGetEntityManager.class, EntityExtendingEntityWithStaticGetEntityManager.class ); + } + + @Test + @WithClasses({ + NonEntityWithInstanceGetEntityManager.class, EntityExtendingNonEntityWithInstanceGetEntityManager.class + }) + public void nonEntityWithInstanceGetEntityManager() { + doTest( + NonEntityWithInstanceGetEntityManager.class, + EntityExtendingNonEntityWithInstanceGetEntityManager.class + ); + } + + @Test + @WithClasses({ + NonEntityWithStaticGetEntityManager.class, + EntityExtendingNonEntityWithStaticGetEntityManager.class + }) + public void nonEntityWithStaticGetEntityManager() { + doTest( + NonEntityWithStaticGetEntityManager.class, + EntityExtendingNonEntityWithStaticGetEntityManager.class + ); + } + + @Test + @WithClasses({ + MapperSuperClassWithInstanceGetEntityManager.class, + EntityExtendingMapperSuperClassWithInstanceGetEntityManager.class + }) + public void mappedSuperClassWithInstanceGetEntityManager() { + doTest( + MapperSuperClassWithInstanceGetEntityManager.class, + EntityExtendingMapperSuperClassWithInstanceGetEntityManager.class + ); + } + + @Test + @WithClasses({ + MapperSuperClassWithStaticGetEntityManager.class, + EntityExtendingMapperSuperClassWithStaticGetEntityManager.class + }) + public void mappedSuperClassWithStaticGetEntityManager() { + doTest( + MapperSuperClassWithStaticGetEntityManager.class, + EntityExtendingMapperSuperClassWithStaticGetEntityManager.class + ); + } + + @Test + @WithClasses({ + NonEntityWithInstanceGetEntityManager.class, + MapperSuperClassExtendingNonEntityWithInstanceGetEntityManager.class, + EntityExtendingMapperSuperClassExtendingNonEntityWithInstanceGetEntityManager.class + }) + public void mappedSuperClassExtendingNonEntityWithInstanceGetEntityManager() { + doTest( + MapperSuperClassExtendingNonEntityWithInstanceGetEntityManager.class, + EntityExtendingMapperSuperClassExtendingNonEntityWithInstanceGetEntityManager.class + ); + } + + // NOTE: only this test matches the Panache use case exactly. + // see https://github.com/quarkusio/quarkus/issues/38378#issuecomment-1911702314 + @Test + @WithClasses({ + NonEntityWithStaticGetEntityManager.class, + MapperSuperClassExtendingNonEntityWithStaticGetEntityManager.class, + EntityExtendingMapperSuperClassExtendingNonEntityWithStaticGetEntityManager.class + }) + public void mappedSuperClassExtendingNonEntityWithStaticGetEntityManager() { + doTest( + MapperSuperClassExtendingNonEntityWithStaticGetEntityManager.class, + EntityExtendingMapperSuperClassExtendingNonEntityWithStaticGetEntityManager.class + ); + } + + private void doTest(Class superclass, Class entitySubclass) { + System.out.println( TestUtil.getMetaModelSourceAsString( superclass ) ); + System.out.println( TestUtil.getMetaModelSourceAsString( entitySubclass ) ); + assertMetamodelClassGeneratedFor( entitySubclass ); + assertAbsenceOfNonDefaultConstructorInMetamodelFor( + entitySubclass, + "The generated metamodel shouldn't include a non-default constructor. In particular, it shouldn't be injected with an entity manager." + ); + } +} + diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/util/TestUtil.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/util/TestUtil.java index a255e5046fc4..c4b7f3e362ea 100644 --- a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/util/TestUtil.java +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/util/TestUtil.java @@ -6,11 +6,18 @@ */ package org.hibernate.jpamodelgen.test.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.BufferedReader; import java.io.File; import java.io.FileFilter; import java.io.FileReader; import java.io.IOException; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; @@ -18,14 +25,14 @@ import java.lang.reflect.Type; import java.net.URL; import java.net.URLClassLoader; +import java.util.Arrays; import java.util.List; -import jakarta.persistence.metamodel.ListAttribute; -import jakarta.persistence.metamodel.SetAttribute; import javax.tools.Diagnostic; import org.jboss.logging.Logger; -import static org.junit.Assert.*; +import jakarta.persistence.metamodel.ListAttribute; +import jakarta.persistence.metamodel.SetAttribute; /** * @author Hardy Ferentschik @@ -59,6 +66,10 @@ public static void assertNoSourceFileGeneratedFor(Class clazz) { assertFalse( "There should be no source file: " + sourceFile.getName(), sourceFile.exists() ); } + public static void assertAbsenceOfNonDefaultConstructorInMetamodelFor(Class clazz, String errorString) { + assertFalse(buildErrorString( errorString, clazz ), hasNonDefaultConstructorInMetamodelFor( clazz ) ); + } + public static void assertAbsenceOfFieldInMetamodelFor(Class clazz, String fieldName) { assertAbsenceOfFieldInMetamodelFor( clazz, @@ -92,16 +103,21 @@ public static void assertPresenceOfFieldInMetamodelFor(Class clazz, String fi assertTrue( buildErrorString( errorString, clazz ), hasFieldInMetamodelFor( clazz, fieldName ) ); } - public static void assertPresenceOfMethodInMetamodelFor(Class clazz, String fieldName, String errorString, Class... params) { + public static void assertPresenceOfMethodInMetamodelFor(Class clazz, String fieldName, String errorString, + Class... params) { assertTrue( buildErrorString( errorString, clazz ), hasMethodInMetamodelFor( clazz, fieldName, params ) ); } public static void assertPresenceOfNameFieldInMetamodelFor(Class clazz, String fieldName, String errorString) { assertTrue( buildErrorString( errorString, clazz ), hasFieldInMetamodelFor( clazz, fieldName ) ); - assertEquals(buildErrorString(errorString, clazz), getFieldFromMetamodelFor(clazz, fieldName).getType(), String.class); + assertEquals( + buildErrorString( errorString, clazz ), getFieldFromMetamodelFor( clazz, fieldName ).getType(), + String.class + ); } - public static void assertAttributeTypeInMetaModelFor(Class clazz, String fieldName, Class expectedType, String errorString) { + public static void assertAttributeTypeInMetaModelFor(Class clazz, String fieldName, Class expectedType, + String errorString) { Field field = getFieldFromMetamodelFor( clazz, fieldName ); assertNotNull( "Cannot find field '" + fieldName + "' in " + clazz.getName(), field ); ParameterizedType type = (ParameterizedType) field.getGenericType(); @@ -117,7 +133,8 @@ public static void assertAttributeTypeInMetaModelFor(Class clazz, String fiel ); } - public static void assertAttributeTypeInMetaModelFor(Class clazz, String fieldName, Type expectedType, String errorString) { + public static void assertAttributeTypeInMetaModelFor(Class clazz, String fieldName, Type expectedType, + String errorString) { Field field = getFieldFromMetamodelFor( clazz, fieldName ); assertNotNull( "Cannot find field '" + fieldName + "' in " + clazz.getName(), field ); ParameterizedType type = (ParameterizedType) field.getGenericType(); @@ -129,15 +146,18 @@ public static void assertAttributeTypeInMetaModelFor(Class clazz, String fiel ); } - public static void assertSetAttributeTypeInMetaModelFor(Class clazz, String fieldName, Class expectedType, String errorString) { + public static void assertSetAttributeTypeInMetaModelFor(Class clazz, String fieldName, Class expectedType, + String errorString) { assertCollectionAttributeTypeInMetaModelFor( clazz, fieldName, SetAttribute.class, expectedType, errorString ); } - public static void assertListAttributeTypeInMetaModelFor(Class clazz, String fieldName, Class expectedType, String errorString) { + public static void assertListAttributeTypeInMetaModelFor(Class clazz, String fieldName, Class expectedType, + String errorString) { assertCollectionAttributeTypeInMetaModelFor( clazz, fieldName, ListAttribute.class, expectedType, errorString ); } - public static void assertMapAttributesInMetaModelFor(Class clazz, String fieldName, Class expectedMapKey, Class expectedMapValue, String errorString) { + public static void assertMapAttributesInMetaModelFor(Class clazz, String fieldName, Class expectedMapKey, + Class expectedMapValue, String errorString) { Field field = getFieldFromMetamodelFor( clazz, fieldName ); assertNotNull( field ); ParameterizedType type = (ParameterizedType) field.getGenericType(); @@ -180,7 +200,8 @@ public static void assertNoMetamodelClassGeneratedFor(Class clazz) { getMetamodelClassFor( clazz ); fail(); } - catch (AssertionError ae) {} + catch (AssertionError ae) { + } } /** @@ -216,7 +237,7 @@ public static Class getMetamodelClassFor(Class entityClass) { URLClassLoader classLoader = new URLClassLoader( urls, TestUtil.class.getClassLoader() ); return classLoader.loadClass( metaModelClassName ); } - catch ( Exception e ) { + catch (Exception e) { fail( metaModelClassName + " was not generated." ); } // keep the compiler happy @@ -240,11 +261,11 @@ public static String getMetaModelSourceAsString(Class clazz) { try { String line; /* - * readLine is a bit quirky : - * it returns the content of a line MINUS the newline. - * it returns null only for the END of the stream. - * it returns an empty String if two newlines appear in a row. - */ + * readLine is a bit quirky : + * it returns the content of a line MINUS the newline. + * it returns null only for the END of the stream. + * it returns an empty String if two newlines appear in a row. + */ while ( ( line = input.readLine() ) != null ) { contents.append( line ); contents.append( System.lineSeparator() ); @@ -254,7 +275,7 @@ public static String getMetaModelSourceAsString(Class clazz) { input.close(); } } - catch ( IOException ex ) { + catch (IOException ex) { ex.printStackTrace(); } @@ -266,12 +287,17 @@ public static void dumpMetaModelSourceFor(Class clazz) { log.info( getMetaModelSourceAsString( clazz ) ); } + public static Constructor[] getConstructorsFromMetamodelFor(Class entityClass) { + Class metaModelClass = getMetamodelClassFor( entityClass ); + return metaModelClass.getConstructors(); + } + public static Field getFieldFromMetamodelFor(Class entityClass, String fieldName) { Class metaModelClass = getMetamodelClassFor( entityClass ); try { return metaModelClass.getDeclaredField( fieldName ); } - catch ( NoSuchFieldException e ) { + catch (NoSuchFieldException e) { return null; } } @@ -281,7 +307,7 @@ public static Method getMethodFromMetamodelFor(Class entityClass, String meth try { return metaModelClass.getDeclaredMethod( methodName, params ); } - catch ( NoSuchMethodException e ) { + catch (NoSuchMethodException e) { return null; } } @@ -290,6 +316,11 @@ public static String fcnToPath(String fcn) { return fcn.replace( PACKAGE_SEPARATOR, RESOURCE_SEPARATOR ); } + private static boolean hasNonDefaultConstructorInMetamodelFor(Class clazz) { + return Arrays.stream( getConstructorsFromMetamodelFor( clazz ) ) + .anyMatch( constructor -> constructor.getParameterCount() > 0 ); + } + private static boolean hasFieldInMetamodelFor(Class clazz, String fieldName) { return getFieldFromMetamodelFor( clazz, fieldName ) != null; } From 74f018182e69afc05900ddbc82a6983886f0366b Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 29 Jan 2024 18:58:36 +0100 Subject: [PATCH 081/321] HHH-17587 Add test for issue --- .../SecondaryTableDynamicUpateTest.java | 149 +++++++++++++++++ .../SecondaryTableDynamicUpateTest.java | 153 ++++++++++++++++++ 2 files changed, 302 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/secondarytable/SecondaryTableDynamicUpateTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/secondarytables/SecondaryTableDynamicUpateTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/secondarytable/SecondaryTableDynamicUpateTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/secondarytable/SecondaryTableDynamicUpateTest.java new file mode 100644 index 000000000000..7e35bd7319ba --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/secondarytable/SecondaryTableDynamicUpateTest.java @@ -0,0 +1,149 @@ +package org.hibernate.orm.test.annotations.secondarytable; + +import org.hibernate.annotations.DynamicUpdate; + +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.PrimaryKeyJoinColumn; +import jakarta.persistence.SecondaryTable; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@Jpa( + annotatedClasses = { + SecondaryTableDynamicUpateTest.TestEntity.class + } +) +@JiraKey("HHH-17587") +public class SecondaryTableDynamicUpateTest { + + private static final Long ENTITY_ID = 123l; + private static final String COL_VALUE = "col"; + private static final String COL1_VALUE = "col1"; + private static final String COL2_VALUE = "col2"; + + @BeforeAll + public static void setUp(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + TestEntity testEntity = new TestEntity( ENTITY_ID, COL_VALUE, COL1_VALUE, COL2_VALUE ); + entityManager.persist( testEntity ); + } + ); + } + + @Test + public void testSetSecondaryTableColumnToNull(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + TestEntity testEntity = entityManager.find( TestEntity.class, ENTITY_ID ); + assertThat( testEntity.getTestCol() ).isEqualTo( COL_VALUE ); + assertThat( testEntity.getTestCol1() ).isEqualTo( COL1_VALUE ); + assertThat( testEntity.getTestCol2() ).isEqualTo( COL2_VALUE ); + testEntity.setTestCol1( null ); + } + ); + + scope.inTransaction( + entityManager -> { + TestEntity testEntity = entityManager.find( TestEntity.class, ENTITY_ID ); + assertThat( testEntity ).isNotNull(); + assertThat( testEntity.getTestCol() ).isEqualTo( COL_VALUE ); + assertThat( testEntity.getTestCol1() ).isNull(); + assertThat( testEntity.getTestCol2() ).isEqualTo( COL2_VALUE ); + testEntity.setTestCol2( null ); + } + ); + + scope.inTransaction( + entityManager -> { + TestEntity testEntity = entityManager.find( TestEntity.class, ENTITY_ID ); + assertThat( testEntity ).isNotNull(); + assertThat( testEntity.getTestCol() ).isEqualTo( COL_VALUE ); + assertThat( testEntity.getTestCol1() ).isNull(); + assertThat( testEntity.getTestCol2() ).isNull(); + testEntity.setTestCol1( COL1_VALUE ); + testEntity.setTestCol( null ); + } + ); + + scope.inTransaction( + entityManager -> { + TestEntity testEntity = entityManager.find( TestEntity.class, ENTITY_ID ); + assertThat( testEntity ).isNotNull(); + assertThat( testEntity.getTestCol() ).isNull(); + assertThat( testEntity.getTestCol1() ).isEqualTo( COL1_VALUE ); + assertThat( testEntity.getTestCol2() ).isNull(); + testEntity.setTestCol2( null ); + } + ); + } + + @Entity(name = "TestEntity") + @SecondaryTable(name = "SECOND_TABLE_TEST", pkJoinColumns = @PrimaryKeyJoinColumn(name = "ID")) + @DynamicUpdate + public static class TestEntity { + + @Id + private Long id; + + @Column(name = "TEST_COL") + private String testCol; + + @Column(name = "TESTCOL1", table = "SECOND_TABLE_TEST") + private String testCol1; + + @Column(name = "TESTCOL2", table = "SECOND_TABLE_TEST") + private String testCol2; + + public TestEntity() { + } + + public TestEntity(Long id, String testCol, String testCol1, String testCol2) { + this.id = id; + this.testCol = testCol; + this.testCol1 = testCol1; + this.testCol2 = testCol2; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTestCol() { + return testCol; + } + + public void setTestCol(String testCol) { + this.testCol = testCol; + } + + public String getTestCol1() { + return testCol1; + } + + public void setTestCol1(String testCol1) { + this.testCol1 = testCol1; + } + + public String getTestCol2() { + return testCol2; + } + + public void setTestCol2(String testCol2) { + this.testCol2 = testCol2; + } + + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/secondarytables/SecondaryTableDynamicUpateTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/secondarytables/SecondaryTableDynamicUpateTest.java new file mode 100644 index 000000000000..0ef090965e10 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/secondarytables/SecondaryTableDynamicUpateTest.java @@ -0,0 +1,153 @@ +package org.hibernate.orm.test.bytecode.enhancement.secondarytables; + +import org.hibernate.annotations.DynamicUpdate; + +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.orm.junit.JiraKey; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.PrimaryKeyJoinColumn; +import jakarta.persistence.SecondaryTable; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@JiraKey("HHH-17587") +@RunWith(BytecodeEnhancerRunner.class) +public class SecondaryTableDynamicUpateTest extends BaseCoreFunctionalTestCase { + + private static final Long ENTITY_ID = 123l; + private static final String COL_VALUE = "col"; + private static final String COL1_VALUE = "col1"; + private static final String COL2_VALUE = "col2"; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + TestEntity.class + }; + } + + @Before + public void setUp() { + inTransaction( + entityManager -> { + TestEntity testEntity = new TestEntity( ENTITY_ID, COL_VALUE, COL1_VALUE, COL2_VALUE ); + entityManager.persist( testEntity ); + } + ); + } + + @Test + public void testSetSecondaryTableColumnToNull() { + inTransaction( + entityManager -> { + TestEntity testEntity = entityManager.find( TestEntity.class, ENTITY_ID ); + assertThat( testEntity.getTestCol() ).isEqualTo( COL_VALUE ); + assertThat( testEntity.getTestCol1() ).isEqualTo( COL1_VALUE ); + assertThat( testEntity.getTestCol2() ).isEqualTo( COL2_VALUE ); + testEntity.setTestCol1( null ); + } + ); + + inTransaction( + entityManager -> { + TestEntity testEntity = entityManager.find( TestEntity.class, ENTITY_ID ); + assertThat( testEntity ).isNotNull(); + assertThat( testEntity.getTestCol() ).isEqualTo( COL_VALUE ); + assertThat( testEntity.getTestCol1() ).isNull(); + assertThat( testEntity.getTestCol2() ).isEqualTo( COL2_VALUE ); + testEntity.setTestCol2( null ); + } + ); + + inTransaction( + entityManager -> { + TestEntity testEntity = entityManager.find( TestEntity.class, ENTITY_ID ); + assertThat( testEntity ).isNotNull(); + assertThat( testEntity.getTestCol() ).isEqualTo( COL_VALUE ); + assertThat( testEntity.getTestCol1() ).isNull(); + assertThat( testEntity.getTestCol2() ).isNull(); + testEntity.setTestCol1( COL1_VALUE ); + testEntity.setTestCol( null ); + } + ); + + inTransaction( + entityManager -> { + TestEntity testEntity = entityManager.find( TestEntity.class, ENTITY_ID ); + assertThat( testEntity ).isNotNull(); + assertThat( testEntity.getTestCol() ).isNull(); + assertThat( testEntity.getTestCol1() ).isEqualTo( COL1_VALUE ); + assertThat( testEntity.getTestCol2() ).isNull(); + testEntity.setTestCol2( null ); + } + ); + } + + @Entity(name = "TestEntity") + @SecondaryTable(name = "SECOND_TABLE_TEST", pkJoinColumns = @PrimaryKeyJoinColumn(name = "ID")) + @DynamicUpdate + public static class TestEntity { + + @Id + private Long id; + + @Column(name = "TEST_COL") + private String testCol; + + @Column(name = "TESTCOL1", table = "SECOND_TABLE_TEST") + private String testCol1; + + @Column(name = "TESTCOL2", table = "SECOND_TABLE_TEST") + private String testCol2; + + public TestEntity() { + } + + public TestEntity(Long id, String testCol, String testCol1, String testCol2) { + this.id = id; + this.testCol = testCol; + this.testCol1 = testCol1; + this.testCol2 = testCol2; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTestCol() { + return testCol; + } + + public void setTestCol(String testCol) { + this.testCol = testCol; + } + + public String getTestCol1() { + return testCol1; + } + + public void setTestCol1(String testCol1) { + this.testCol1 = testCol1; + } + + public String getTestCol2() { + return testCol2; + } + + public void setTestCol2(String testCol2) { + this.testCol2 = testCol2; + } + + } +} From 3844a34760dfc33ff70e69928491e6d08cddc297 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 30 Jan 2024 13:50:41 +0100 Subject: [PATCH 082/321] HHH-17587 Setting to null a property from a @SecondaryTable and @DynamicUpdate deletes the whole entry from database --- .../entity/AbstractEntityPersister.java | 16 ++++++++--- .../entity/mutation/EntityTableMapping.java | 27 ++++++++++++++++--- .../mutation/UpdateCoordinatorStandard.java | 6 +++-- .../entity/mutation/UpdateValuesAnalysis.java | 2 ++ .../org/hibernate/sql/model/TableMapping.java | 22 +++++++++++++++ .../model/internal/OptionalTableUpdate.java | 10 +++++-- .../jdbc/OptionalTableUpdateOperation.java | 3 ++- 7 files changed, 74 insertions(+), 12 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 945d395cbd6e..9aa98b3139ab 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -3479,6 +3479,8 @@ private static class TableMappingBuilder { private final Expectation deleteExpectation; private final String customDeleteSql; private final boolean deleteCallable; + private final boolean dynamicUpdate; + private final boolean dynamicInsert; private final List attributeIndexes = new ArrayList<>(); @@ -3498,7 +3500,9 @@ public TableMappingBuilder( boolean cascadeDeleteEnabled, Expectation deleteExpectation, String customDeleteSql, - boolean deleteCallable) { + boolean deleteCallable, + boolean dynamicUpdate, + boolean dynamicInsert) { this.tableName = tableName; this.relativePosition = relativePosition; this.keyMapping = keyMapping; @@ -3515,6 +3519,8 @@ public TableMappingBuilder( this.deleteExpectation = deleteExpectation; this.customDeleteSql = customDeleteSql; this.deleteCallable = deleteCallable; + this.dynamicUpdate = dynamicUpdate; + this.dynamicInsert = dynamicInsert; } private EntityTableMapping build() { @@ -3535,7 +3541,9 @@ private EntityTableMapping build() { cascadeDeleteEnabled, deleteExpectation, customDeleteSql, - deleteCallable + deleteCallable, + dynamicUpdate, + dynamicInsert ); } } @@ -3592,7 +3600,9 @@ protected EntityTableMapping[] buildTableMappings() { isTableCascadeDeleteEnabled( relativePosition ), deleteExpectations[ relativePosition ], customDeleteSql, - deleteCallable[ relativePosition ] + deleteCallable[ relativePosition ], + entityMetamodel.isDynamicUpdate(), + entityMetamodel.isDynamicInsert() ); tableBuilderMap.put( tableExpression, tableMappingBuilder ); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/EntityTableMapping.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/EntityTableMapping.java index 24d8599f03ae..74a2215147a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/EntityTableMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/EntityTableMapping.java @@ -65,14 +65,33 @@ public EntityTableMapping( boolean cascadeDeleteEnabled, Expectation deleteExpectation, String deleteCustomSql, - boolean deleteCallable) { + boolean deleteCallable, + boolean dynamicUpdate, + boolean dynamicInsert) { this.tableName = tableName; this.relativePosition = relativePosition; this.keyMapping = keyMapping; this.attributeIndexes = attributeIndexes; - this.insertDetails = new MutationDetails( MutationType.INSERT, insertExpectation, insertCustomSql, insertCallable ); - this.updateDetails = new MutationDetails( MutationType.UPDATE, updateExpectation, updateCustomSql, updateCallable ); - this.deleteDetails = new MutationDetails( MutationType.DELETE, deleteExpectation, deleteCustomSql, deleteCallable ); + this.insertDetails = new MutationDetails( + MutationType.INSERT, + insertExpectation, + insertCustomSql, + insertCallable, + dynamicInsert + ); + this.updateDetails = new MutationDetails( + MutationType.UPDATE, + updateExpectation, + updateCustomSql, + updateCallable, + dynamicUpdate + ); + this.deleteDetails = new MutationDetails( + MutationType.DELETE, + deleteExpectation, + deleteCustomSql, + deleteCallable + ); if ( isOptional ) { flags.set( Flag.OPTIONAL.ordinal() ); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java index 29ee5ac2d5e9..716eaa483578 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java @@ -305,7 +305,7 @@ protected void performUpdate( ); //noinspection StatementWithEmptyBody - if ( valuesAnalysis.tablesNeedingUpdate.isEmpty() ) { + if ( valuesAnalysis.tablesNeedingUpdate.isEmpty() && valuesAnalysis.tablesNeedingDynamicUpdate.isEmpty() ) { // nothing to do } else if ( valuesAnalysis.needsDynamicUpdate() ) { @@ -969,7 +969,9 @@ protected void doDynamicUpdate( if ( tableMapping.isOptional() && !valuesAnalysis.tablesWithNonNullValues.contains( tableMapping ) ) { // the table is optional, and we have null values for all of its columns - // todo (6.0) : technically we might need to delete row here + if ( valuesAnalysis.dirtyAttributeIndexes.length > 0 ) { + return true; + } return false; } else { diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateValuesAnalysis.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateValuesAnalysis.java index 9b5eff3533fe..02e75326ca51 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateValuesAnalysis.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateValuesAnalysis.java @@ -40,6 +40,8 @@ public interface UpdateValuesAnalysis extends ValuesAnalysis { */ TableSet getTablesWithPreviousNonNullValues(); + TableSet getTablesNeedingDynamicUpdate(); + /** * Descriptors for the analysis of each attribute */ diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/TableMapping.java b/hibernate-core/src/main/java/org/hibernate/sql/model/TableMapping.java index 60de5d041ccd..50eebe1b27ef 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/model/TableMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/TableMapping.java @@ -81,16 +81,33 @@ class MutationDetails { private final Expectation expectation; private final String customSql; private final boolean callable; + private final boolean dynamicMutation; public MutationDetails( MutationType mutationType, Expectation expectation, String customSql, boolean callable) { + this( + mutationType, + expectation, + customSql, + callable, + false + ); + } + + public MutationDetails( + MutationType mutationType, + Expectation expectation, + String customSql, + boolean callable, + boolean dynamicMutation) { this.mutationType = mutationType; this.expectation = expectation; this.customSql = customSql; this.callable = callable; + this.dynamicMutation = dynamicMutation; } /** @@ -122,5 +139,10 @@ public String getCustomSql() { public boolean isCallable() { return callable; } + + public boolean isDynamicMutation() { + return dynamicMutation; + } } + } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/internal/OptionalTableUpdate.java b/hibernate-core/src/main/java/org/hibernate/sql/model/internal/OptionalTableUpdate.java index fd429a8102eb..f2e777081283 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/model/internal/OptionalTableUpdate.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/internal/OptionalTableUpdate.java @@ -128,9 +128,15 @@ public void accept(SqlAstWalker walker) { @Override public MutationOperation createMutationOperation(ValuesAnalysis valuesAnalysis, SessionFactoryImplementor factory) { - if ( getMutatingTable().getTableMapping().getInsertDetails().getCustomSql() != null - || getMutatingTable().getTableMapping().getDeleteDetails().getCustomSql() != null ) { + final TableMapping tableMapping = getMutatingTable().getTableMapping(); + if ( tableMapping.getInsertDetails().getCustomSql() != null + || tableMapping.getInsertDetails().isDynamicMutation() + || tableMapping.getDeleteDetails().getCustomSql() != null + || tableMapping.getUpdateDetails().getCustomSql() != null + || tableMapping.getUpdateDetails().isDynamicMutation() ) { // Fallback to the optional table mutation operation because we have to execute user specified SQL + // and with dynamic update insert we have to avoid using merge for optional table because + // it can cause the deletion of the row when an attribute is set to null return new OptionalTableUpdateOperation( getMutationTarget(), this, factory ); } return factory.getJdbcServices().getDialect().createOptionalTableUpdateOperation( diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateOperation.java b/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateOperation.java index bc0d018f6214..25fa3ef0cf95 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateOperation.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateOperation.java @@ -127,7 +127,8 @@ public void performMutation( ValuesAnalysis incomingValuesAnalysis, SharedSessionContractImplementor session) { final UpdateValuesAnalysis valuesAnalysis = (UpdateValuesAnalysis) incomingValuesAnalysis; - if ( !valuesAnalysis.getTablesNeedingUpdate().contains( tableMapping ) ) { + if ( !valuesAnalysis.getTablesNeedingUpdate().contains( tableMapping ) + && !valuesAnalysis.getTablesNeedingDynamicUpdate().contains( tableMapping ) ) { return; } From 652da4205fc16b18e675041ea822dc282833d834 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Fri, 5 Jan 2024 11:58:51 +0100 Subject: [PATCH 083/321] HHH-17594 HHH-17665 Add test for issue --- .../BidirectionalManyToOneNarrowingTest.java | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/proxy/narrow/BidirectionalManyToOneNarrowingTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/narrow/BidirectionalManyToOneNarrowingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/narrow/BidirectionalManyToOneNarrowingTest.java new file mode 100644 index 000000000000..857df4cf3f3b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/narrow/BidirectionalManyToOneNarrowingTest.java @@ -0,0 +1,199 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.proxy.narrow; + +import java.util.HashSet; +import java.util.Set; + +import org.hibernate.Hibernate; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; + +import static jakarta.persistence.FetchType.LAZY; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Marco Belladelli + */ +@DomainModel( annotatedClasses = { + BidirectionalManyToOneNarrowingTest.Address.class, + BidirectionalManyToOneNarrowingTest.MyAddress.class, + BidirectionalManyToOneNarrowingTest.Message.class, + BidirectionalManyToOneNarrowingTest.AddressContainer.class +} ) +@SessionFactory +@Jira( "https://hibernate.atlassian.net/browse/HHH-17594" ) +@Jira( "https://hibernate.atlassian.net/browse/HHH-17665" ) +public class BidirectionalManyToOneNarrowingTest { + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final MyAddress address = new MyAddress( 1L ); + session.persist( address ); + session.persist( new Message( 2L, address ) ); + final AddressContainer relation = new AddressContainer(); + relation.setId( 3L ); + relation.setMyAddress( address ); + session.persist( relation ); + } ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.createMutationQuery( "delete from Message" ).executeUpdate(); + session.createMutationQuery( "delete from AddressContainer" ).executeUpdate(); + session.createMutationQuery( "delete from Address" ).executeUpdate(); + } ); + } + + @Test + public void testQuery(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final Message result = session.createQuery( "from Message", Message.class ).getSingleResult(); + assertResult( result ); + } ); + } + + @Test + public void testFind(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final Message result = session.find( Message.class, 2L ); + assertResult( result ); + } ); + } + + private void assertResult(Message result) { + final Address address = result.getReceiverAddress(); + assertThat( Hibernate.isInitialized( address ) ).isFalse(); + assertThat( Hibernate.getClass( address ) ).isEqualTo( MyAddress.class ); + final MyAddress myAddress = (MyAddress) Hibernate.unproxy( address ); + final Set relations = myAddress.getAddressUserRelations(); + assertThat( relations ).hasSize( 1 ); + final MyAddress relatedAddress = relations.iterator().next().getMyAddress(); + assertThat( relatedAddress.getId() ).isEqualTo( 1L ); + assertThat( relatedAddress ).isSameAs( myAddress ); + } + + @Test + public void testProxyReuse(SessionFactoryScope scope) { + // uninitialized proxy + scope.inTransaction( session -> { + final Address address = session.getReference( Address.class, 1L ); + assertThat( Hibernate.isInitialized( address ) ).isFalse(); + final AddressContainer addressContainer = session.find( AddressContainer.class, 3L ); + final MyAddress myAddress = addressContainer.getMyAddress(); + assertThat( Hibernate.isInitialized( myAddress ) ).isFalse(); + assertThat( myAddress.getId() ).isEqualTo( address.getId() ); + } ); + // initialized proxy + scope.inTransaction( session -> { + final Address address = session.getReference( Address.class, 1L ); + assertThat( Hibernate.getClass( address ) ).isEqualTo( MyAddress.class ); + assertThat( Hibernate.isInitialized( address ) ).isTrue(); + final AddressContainer addressContainer = session.find( AddressContainer.class, 3L ); + final MyAddress myAddress = addressContainer.getMyAddress(); + assertThat( Hibernate.isInitialized( myAddress ) ).isTrue(); + assertThat( myAddress.getId() ).isEqualTo( address.getId() ); + } ); + } + + @Entity( name = "Address" ) + public static abstract class Address { + @Id + private Long id; + + public Address() { + } + + public Address(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + } + + @Entity( name = "Message" ) + public static class Message { + @Id + private Long id; + + @ManyToOne( fetch = FetchType.LAZY ) + @JoinColumn( name = "receiver_address_id" ) + private Address receiverAddress; + + public Message() { + } + + public Message(Long id, Address receiverAddress) { + this.id = id; + this.receiverAddress = receiverAddress; + } + + public Address getReceiverAddress() { + return receiverAddress; + } + } + + @Entity( name = "MyAddress" ) + public static class MyAddress extends Address { + @OneToMany( mappedBy = "myAddress" ) + private Set addressContainers = new HashSet<>(); + + public MyAddress() { + } + + public MyAddress(Long id) { + super( id ); + } + + public Set getAddressUserRelations() { + return addressContainers; + } + } + + @Entity( name = "AddressContainer" ) + public static class AddressContainer { + @Id + private Long id; + + @ManyToOne( fetch = LAZY ) + @JoinColumn( name = "address_id" ) + private MyAddress myAddress; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public MyAddress getMyAddress() { + return myAddress; + } + + public void setMyAddress(MyAddress myAddress) { + this.myAddress = myAddress; + } + } +} From ff0b5354356f2890e0f297adeff636c564eab155 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Fri, 5 Jan 2024 11:30:46 +0100 Subject: [PATCH 084/321] HHH-17594 HHH-17665 Fix proxy narrowing for delayed subtype entities --- .../internal/StatefulPersistenceContext.java | 9 +++++++-- .../hibernate/engine/spi/PersistenceContext.java | 16 +++++++++++++--- .../internal/EntityDelayedFetchInitializer.java | 2 +- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index f3e9bd8ea9fb..89feecc283f6 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -898,9 +898,14 @@ public Object proxyFor(Object impl) throws HibernateException { @Override public Object proxyFor(EntityHolder holder) throws HibernateException { + return proxyFor( holder, holder.getDescriptor() ); + } + + @Override + public Object proxyFor(EntityHolder holder, EntityPersister persister) { final Object proxy = holder.getProxy(); - return proxy != null && holder.getDescriptor().hasProxy() - ? narrowProxy( proxy, holder.getDescriptor(), holder.getEntityKey(), holder.getEntity() ) + return proxy != null && persister.hasProxy() + ? narrowProxy( proxy, persister, holder.getEntityKey(), holder.getEntity() ) : holder.getEntity(); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java index 818d1b98b0c2..e23de9bee152 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java @@ -348,10 +348,20 @@ EntityEntry addEntry( Object proxyFor(Object impl); /** - * Return the existing proxy associated with the given {@code EntityKey}, or the - * argument (the entity associated with the key) if no proxy exists. - * (slower than the form above) + * Return the existing {@linkplain EntityHolder#getProxy() proxy} associated with + * the given {@link EntityHolder}, or the {@linkplain EntityHolder#getEntity() entity} + * if no proxy exists. + */ + Object proxyFor(EntityHolder holder, EntityPersister persister); + + /** + * Return the existing {@linkplain EntityHolder#getProxy() proxy} associated with + * the given {@link EntityHolder}, or the {@linkplain EntityHolder#getEntity() entity} + * if it contains no proxy. + * + * @deprecated Use {@link #proxyFor(EntityHolder, EntityPersister)} instead. */ + @Deprecated( forRemoval = true ) Object proxyFor(EntityHolder holder); /** diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java index 2412585216e3..457e4e4a385c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java @@ -103,7 +103,7 @@ public void resolveInstance(RowProcessingState rowProcessingState) { final EntityHolder holder = persistenceContext.getEntityHolder( entityKey ); if ( holder != null && holder.getEntity() != null ) { - entityInstance = persistenceContext.proxyFor( holder ); + entityInstance = persistenceContext.proxyFor( holder, concreteDescriptor ); } } if ( entityInstance == null ) { From 28e5ef81d93ae38e7439d98e3417f75bb1341d52 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 29 Jan 2024 16:52:54 +0100 Subject: [PATCH 085/321] HHH-17326 deprecated lazyLoading no longer working --- .../hibernate-enhance-maven-plugin/plugin-help.xml | 4 ++-- .../src/main/resources/META-INF/maven/plugin.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/org.hibernate.orm.tooling/hibernate-enhance-maven-plugin/plugin-help.xml b/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/org.hibernate.orm.tooling/hibernate-enhance-maven-plugin/plugin-help.xml index be774c652c22..0df69ced2aa0 100644 --- a/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/org.hibernate.orm.tooling/hibernate-enhance-maven-plugin/plugin-help.xml +++ b/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/org.hibernate.orm.tooling/hibernate-enhance-maven-plugin/plugin-help.xml @@ -85,8 +85,8 @@ ${project.build.outputDirectory}

${project.build.outputDirectory} true - false - false + true + true false false diff --git a/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/plugin.xml b/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/plugin.xml index b484c318a5d2..6ffe57657953 100644 --- a/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/plugin.xml +++ b/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/plugin.xml @@ -86,8 +86,8 @@ ${project.build.outputDirectory} ${project.build.outputDirectory} true - false - false + true + true false false From da21ca2e805f18be70e97bc1c53037c774f32534 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Tue, 30 Jan 2024 18:47:00 +0100 Subject: [PATCH 086/321] HHH-17689 Cache SQL statement for unique key lookup --- .../SingleUniqueKeyEntityLoaderStandard.java | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java index d95f4bea3b9a..6cb2bc7569f9 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java @@ -41,6 +41,8 @@ public class SingleUniqueKeyEntityLoaderStandard implements SingleUniqueKeyEntityLoader { private final EntityMappingType entityDescriptor; private final ModelPart uniqueKeyAttribute; + private final JdbcParametersList jdbcParameters; + private final JdbcOperationQuerySelect jdbcSelect; public SingleUniqueKeyEntityLoaderStandard( EntityMappingType entityDescriptor, @@ -52,23 +54,9 @@ public SingleUniqueKeyEntityLoaderStandard( else { this.uniqueKeyAttribute = uniqueKeyAttribute; } - } - - @Override - public EntityMappingType getLoadable() { - return entityDescriptor; - } - - @Override - public T load( - Object ukValue, - LockOptions lockOptions, - Boolean readOnly, - SharedSessionContractImplementor session) { - final SessionFactoryImplementor sessionFactory = session.getFactory(); - // todo (6.0) : cache the SQL AST and JdbcParameters - JdbcParametersList.Builder builder = JdbcParametersList.newBuilder(); + final SessionFactoryImplementor sessionFactory = entityDescriptor.getEntityPersister().getFactory(); + final JdbcParametersList.Builder builder = JdbcParametersList.newBuilder(); final SelectStatement sqlAst = LoaderSelectBuilder.createSelectByUniqueKey( entityDescriptor, Collections.emptyList(), @@ -83,7 +71,23 @@ public T load( final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory(); - JdbcParametersList jdbcParameters = builder.build(); + this.jdbcParameters = builder.build(); + this.jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory, sqlAst ) + .translate( JdbcParameterBindings.NO_BINDINGS, QueryOptions.NONE ); + } + + @Override + public EntityMappingType getLoadable() { + return entityDescriptor; + } + + @Override + public T load( + Object ukValue, + LockOptions lockOptions, + Boolean readOnly, + SharedSessionContractImplementor session) { + final SessionFactoryImplementor sessionFactory = session.getFactory(); final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( jdbcParameters.size() ); int offset = jdbcParameterBindings.registerParametersForEachJdbcValue( @@ -93,9 +97,6 @@ public T load( session ); assert offset == jdbcParameters.size(); - final JdbcOperationQuerySelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory, sqlAst ) - .translate( jdbcParameterBindings, QueryOptions.NONE ); - final List list = sessionFactory.getJdbcServices().getJdbcSelectExecutor().list( jdbcSelect, jdbcParameterBindings, From 7197f25c06a338a27b6eda438bbbd4e7ae1a0c67 Mon Sep 17 00:00:00 2001 From: LLEFEVRE Date: Wed, 31 Jan 2024 14:03:54 +0100 Subject: [PATCH 087/321] HHH-17319 Use Oracle GraalVM for Atlas builds --- .github/workflows/atlas.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/atlas.yml b/.github/workflows/atlas.yml index 941f531021ee..8fdbf39216a4 100644 --- a/.github/workflows/atlas.yml +++ b/.github/workflows/atlas.yml @@ -52,10 +52,10 @@ jobs: RUNID: ${{ github.run_number }} run: ci/database-start.sh - name: Set up Java 11 - uses: actions/setup-java@v4 + uses: graalvm/setup-graalvm@v1 with: - distribution: 'temurin' - java-version: '11' + distribution: 'graalvm' + java-version: '21' - name: Get year/month for cache key id: get-date run: echo "yearmonth=$(/bin/date -u "+%Y-%m")" >> $GITHUB_OUTPUT From 702caf471cb85905ce5481fe2b37b35ee185fab9 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 30 Jan 2024 09:33:56 +0100 Subject: [PATCH 088/321] HHH-17686 Add test for issue --- ...neSingleTableInheritanceAutoFlushTest.java | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/ToOneSingleTableInheritanceAutoFlushTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/ToOneSingleTableInheritanceAutoFlushTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/ToOneSingleTableInheritanceAutoFlushTest.java new file mode 100644 index 000000000000..085b114d50b2 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/ToOneSingleTableInheritanceAutoFlushTest.java @@ -0,0 +1,184 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.inheritance; + +import java.util.List; + +import org.hibernate.testing.jdbc.SQLStatementInspector; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToOne; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Marco Belladelli + */ +@DomainModel( annotatedClasses = { + ToOneSingleTableInheritanceAutoFlushTest.SiteUser.class, + ToOneSingleTableInheritanceAutoFlushTest.Profile.class, + ToOneSingleTableInheritanceAutoFlushTest.CommunityProfile.class, +} ) +@SessionFactory( useCollectingStatementInspector = true ) +@Jira( "https://hibernate.atlassian.net/browse/HHH-17686" ) +public class ToOneSingleTableInheritanceAutoFlushTest { + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final Profile profile = new Profile( 1L, "profile_1" ); + session.persist( profile ); + final CommunityProfile communityProfile = new CommunityProfile( 2L, "community_2", "community_2" ); + session.persist( communityProfile ); + final SiteUser siteUser = new SiteUser(); + siteUser.setProfile( profile ); + siteUser.setCommunityProfile( communityProfile ); + session.persist( siteUser ); + } ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.createMutationQuery( "delete from SiteUser" ).executeUpdate(); + session.createMutationQuery( "delete from Profile" ).executeUpdate(); + } ); + } + + @Test + public void testSupertypeAutoFlush(SessionFactoryScope scope) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + scope.inTransaction( session -> { + final Profile profile = session.find( Profile.class, 1L ); + inspector.clear(); + + profile.setName( "new_profile_1" ); + final List resultList = session.createQuery( + "from SiteUser u join u.profile p where p.name = 'new_profile_1'", + SiteUser.class + ).getResultList(); + assertThat( resultList ).hasSize( 1 ); + assertThat( resultList.get( 0 ).getProfile().getName() ).isEqualTo( "new_profile_1" ); + inspector.assertIsUpdate( 0 ); + inspector.assertIsSelect( 1 ); + } ); + } + + @Test + public void testSubtypeAutoFlush(SessionFactoryScope scope) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + scope.inTransaction( session -> { + final CommunityProfile communityProfile = session.find( CommunityProfile.class, 2L ); + inspector.clear(); + + communityProfile.setCommunity( "new_community_2" ); + final List resultList = session.createQuery( + "from SiteUser u join u.communityProfile p where p.community = 'new_community_2'", + SiteUser.class + ).getResultList(); + assertThat( resultList ).hasSize( 1 ); + assertThat( resultList.get( 0 ).getCommunityProfile().getCommunity() ).isEqualTo( "new_community_2" ); + inspector.assertIsUpdate( 0 ); + inspector.assertIsSelect( 1 ); + } ); + } + + @Entity( name = "SiteUser" ) + public static class SiteUser { + @Id + @GeneratedValue + private Long id; + + @ManyToOne + private Profile profile; + + @OneToOne + private CommunityProfile communityProfile; + + public Profile getProfile() { + return profile; + } + + public void setProfile(Profile profile) { + this.profile = profile; + } + + public CommunityProfile getCommunityProfile() { + return communityProfile; + } + + public void setCommunityProfile(CommunityProfile communityProfile) { + this.communityProfile = communityProfile; + } + } + + @Entity( name = "Profile" ) + @Inheritance( strategy = InheritanceType.SINGLE_TABLE ) + @DiscriminatorColumn( name = "disc_col" ) + @DiscriminatorValue( "profile" ) + public static class Profile { + @Id + private Long id; + + private String name; + + public Profile() { + } + + public Profile(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String profileName) { + this.name = profileName; + } + } + + @Entity( name = "CommunityProfile" ) + @DiscriminatorValue( "community" ) + public static class CommunityProfile extends Profile { + private String community; + + public CommunityProfile() { + } + + public CommunityProfile(Long id, String name, String community) { + super( id, name ); + this.community = community; + } + + public String getCommunity() { + return community; + } + + public void setCommunity(String community) { + this.community = community; + } + } +} From 24d25cedf83c66104594e04545b87d22cde349ea Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 30 Jan 2024 09:39:37 +0100 Subject: [PATCH 089/321] HHH-17686 Avoid internal use of pruned expression for named references --- .../sql/ast/spi/AbstractSqlAstTranslator.java | 2 +- .../sql/ast/tree/from/NamedTableReference.java | 14 +++++--------- .../sql/ast/tree/from/UnionTableReference.java | 3 --- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index a49af9e106df..8af30b45edc3 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -5710,7 +5710,7 @@ protected boolean rendersTableReferenceAlias(Clause clause) { } protected void registerAffectedTable(NamedTableReference tableReference) { - registerAffectedTable( tableReference.getTableExpression() ); + tableReference.applyAffectedTableNames( this::registerAffectedTable ); } protected void registerAffectedTable(String tableExpression) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/NamedTableReference.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/NamedTableReference.java index d78c1a3c7e10..a7ac572f5b3f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/NamedTableReference.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/NamedTableReference.java @@ -12,6 +12,7 @@ import java.util.function.Consumer; import java.util.function.Function; +import org.hibernate.Internal; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.SqlAstWalker; @@ -60,31 +61,26 @@ public void accept(SqlAstWalker sqlTreeWalker) { sqlTreeWalker.visitNamedTableReference( this ); } - @Override - public void applyAffectedTableNames(Consumer nameCollector) { - nameCollector.accept( getTableExpression() ); - } - @Override public List getAffectedTableNames() { - return Collections.singletonList( getTableExpression() ); + return Collections.singletonList( tableExpression ); } @Override public boolean containsAffectedTableName(String requestedName) { - return isEmpty( requestedName ) || getTableExpression().contains( requestedName ); + return isEmpty( requestedName ) || tableExpression.equals( requestedName ); } @Override public Boolean visitAffectedTableNames(Function nameCollector) { - return nameCollector.apply( getTableExpression() ); + return nameCollector.apply( tableExpression ); } @Override public TableReference resolveTableReference( NavigablePath navigablePath, String tableExpression) { - if ( tableExpression.equals( getTableExpression() ) ) { + if ( this.tableExpression.equals( tableExpression ) ) { return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableReference.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableReference.java index d38bc05f2497..6a84e42d49b8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableReference.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableReference.java @@ -67,9 +67,6 @@ public TableReference getTableReference( } private boolean hasTableExpression(String tableExpression) { - if ( tableExpression.equals( getTableExpression() ) ) { - return true; - } for ( String expression : subclassTableSpaceExpressions ) { if ( tableExpression.equals( expression ) ) { return true; From e4f5926bce57f78023c646deb86148af73b24b9a Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 30 Jan 2024 17:47:28 +0100 Subject: [PATCH 090/321] HHH-17687 Add test for issue --- .../AttributeConverterAndNullTest.java | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/AttributeConverterAndNullTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/AttributeConverterAndNullTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/AttributeConverterAndNullTest.java new file mode 100644 index 000000000000..c29008ea712e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/AttributeConverterAndNullTest.java @@ -0,0 +1,119 @@ +package org.hibernate.orm.test.mapping.converted.converter; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Query; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@DomainModel( + annotatedClasses = { + AttributeConverterAndNullTest.TestEntity.class + } +) +@SessionFactory +@JiraKey("HHH-17687") +public class AttributeConverterAndNullTest { + + @Test + public void testSelectByConnvertedField(SessionFactoryScope scope) { + TheField theField = new TheField( "field1" ); + TestEntity testEntity = new TestEntity( theField ); + scope.inTransaction( + session -> { + session.persist( testEntity ); + } + ); + + scope.inSession( + session -> { + Query query1 = session.createQuery( "FROM TestEntity WHERE myField = :myField" ); + query1.setParameter( "myField", theField ); + System.out.println( query1.getSingleResult() ); + } + ); + + TestEntity testEntity2 = new TestEntity( null ); + scope.inTransaction( + session -> + session.persist( testEntity2 ) + ); + + scope.inSession( + session -> { + Query query2 = session.createQuery( "FROM TestEntity WHERE myField = :myField" ); + query2.setParameter( "myField", null ); + assertThat( query2.getResultList().size() ).isEqualTo( 1 ); + } + ); + } + + public static class MyFieldConverter implements AttributeConverter { + + @Override + public String convertToDatabaseColumn(TheField myField) { + if ( myField == null ) { + return "null"; + } + return myField.getaField(); + } + + @Override + public TheField convertToEntityAttribute(String dbValue) { + return new TheField( dbValue ); + } + } + + @Entity(name = "TestEntity") + public static class TestEntity { + + @Id + @GeneratedValue + private Long id; + + @Convert(converter = MyFieldConverter.class) + private TheField myField; + + public TestEntity() { + } + + public TestEntity(TheField myField) { + this.myField = myField; + } + + public Long getId() { + return id; + } + + public TheField getMyField() { + return myField; + } + } + + + public static class TheField { + private String aField; + + public TheField() { + } + + public TheField(String field1) { + this.aField = field1; + } + + public String getaField() { + return aField; + } + + } + +} From b78159499e6c84ea55f50e4f4d14088a06a1b327 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 30 Jan 2024 17:49:30 +0100 Subject: [PATCH 091/321] HHH-17687 AttributeConverter, query does not use converter to convert 'null' fields --- .../engine/internal/CacheHelper.java | 32 +++++----- .../internal/SimpleForeignKeyDescriptor.java | 25 +------- .../hibernate/query/sqm/internal/SqmUtil.java | 60 +++++++++---------- .../exec/internal/AbstractJdbcParameter.java | 25 +------- .../internal/JdbcParameterBindingsImpl.java | 3 +- .../java/org/hibernate/type/CustomType.java | 32 ++++------ .../converter/spi/BasicValueConverter.java | 6 +- 7 files changed, 63 insertions(+), 120 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/CacheHelper.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/CacheHelper.java index d7c973e73327..2f21b5eddd97 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/CacheHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/CacheHelper.java @@ -6,8 +6,6 @@ */ package org.hibernate.engine.internal; -import java.io.Serializable; - import org.hibernate.cache.MutableCacheKeyBuilder; import org.hibernate.cache.spi.access.CachedDomainDataAccess; import org.hibernate.engine.spi.SessionEventListenerManager; @@ -90,31 +88,29 @@ public static Object fromSharedCache( } return cachedValue; } - public static void addBasicValueToCacheKey( MutableCacheKeyBuilder cacheKey, Object value, JdbcMapping jdbcMapping, SharedSessionContractImplementor session) { - if ( value == null ) { - cacheKey.addValue( null ); - cacheKey.addHashCode( 0 ); - return; - } final BasicValueConverter converter = jdbcMapping.getValueConverter(); - final Serializable disassemble; - final int hashCode; + final Object convertedValue; + final JavaType javaType; if ( converter == null ) { - disassemble = jdbcMapping.getJavaTypeDescriptor().getMutabilityPlan().disassemble( value, session ); - hashCode = ( (JavaType) jdbcMapping.getMappedJavaType() ).extractHashCode( value ); + javaType = jdbcMapping.getJavaTypeDescriptor(); + convertedValue = value; + } + else { + javaType = converter.getRelationalJavaType(); + convertedValue = converter.toRelationalValue( value ); + } + if ( convertedValue == null ) { + cacheKey.addValue( null ); + cacheKey.addHashCode( 0 ); } else { - final Object relationalValue = converter.toRelationalValue( value ); - final JavaType relationalJavaType = converter.getRelationalJavaType(); - disassemble = relationalJavaType.getMutabilityPlan().disassemble( relationalValue, session ); - hashCode = relationalJavaType.extractHashCode( relationalValue ); + cacheKey.addValue( javaType.getMutabilityPlan().disassemble( convertedValue, session ) ); + cacheKey.addHashCode( javaType.extractHashCode( convertedValue ) ); } - cacheKey.addValue( disassemble ); - cacheKey.addHashCode( hashCode ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java index 31a435fc98a9..58797a7fec3f 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java @@ -6,7 +6,6 @@ */ package org.hibernate.metamodel.mapping.internal; -import java.io.Serializable; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -16,6 +15,7 @@ import org.hibernate.cache.MutableCacheKeyBuilder; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.internal.CacheHelper; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.IndexedConsumer; import org.hibernate.metamodel.mapping.AssociationKey; @@ -57,7 +57,6 @@ import org.hibernate.sql.results.graph.FetchOptions; import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.graph.basic.BasicResult; -import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; import org.hibernate.type.descriptor.java.JavaType; /** @@ -468,27 +467,7 @@ public Object disassemble(Object value, SharedSessionContractImplementor session @Override public void addToCacheKey(MutableCacheKeyBuilder cacheKey, Object value, SharedSessionContractImplementor session) { - if ( value == null ) { - return; - } - final JdbcMapping jdbcMapping = getJdbcMapping(); - final BasicValueConverter converter = jdbcMapping.getValueConverter(); - final Serializable disassemble; - final int hashCode; - if ( converter == null ) { - final JavaType javaTypeDescriptor = jdbcMapping.getJavaTypeDescriptor(); - disassemble = javaTypeDescriptor.getMutabilityPlan().disassemble( value, session ); - hashCode = javaTypeDescriptor.extractHashCode( disassemble ); - } - else { - final Object relationalValue = converter.toRelationalValue( value ); - final JavaType relationalJavaType = converter.getRelationalJavaType(); - disassemble = relationalJavaType.getMutabilityPlan().disassemble( relationalValue, session ); - hashCode = relationalJavaType.extractHashCode( relationalValue ); - } - - cacheKey.addValue( disassemble ); - cacheKey.addHashCode( hashCode ); + CacheHelper.addBasicValueToCacheKey( cacheKey, value, getJdbcMapping(), session ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java index 24ef4dd57265..b293e1936203 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java @@ -44,7 +44,6 @@ import org.hibernate.query.sqm.SqmQuerySource; import org.hibernate.query.sqm.spi.JdbcParameterBySqmParameterAccess; import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; -import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmDmlStatement; import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.SqmStatement; @@ -57,12 +56,10 @@ import org.hibernate.query.sqm.tree.from.SqmJoin; import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin; import org.hibernate.query.sqm.tree.from.SqmRoot; -import org.hibernate.query.sqm.tree.select.SqmQueryPart; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.query.sqm.tree.select.SqmSelectableNode; import org.hibernate.query.sqm.tree.select.SqmSortSpecification; import org.hibernate.spi.NavigablePath; -import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.SqlTreeCreationException; import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.from.TableGroup; @@ -350,18 +347,6 @@ else if ( domainParamBinding.isMultiValued() ) { expansionPosition++; } } - else if ( domainParamBinding.getBindValue() == null ) { - for ( int i = 0; i < jdbcParamsBinds.size(); i++ ) { - final JdbcParametersList jdbcParams = jdbcParamsBinds.get( i ); - for ( int j = 0; j < jdbcParams.size(); j++ ) { - final JdbcParameter jdbcParameter = jdbcParams.get( j ); - jdbcParameterBindings.addBinding( - jdbcParameter, - new JdbcParameterBindingImpl( null, null ) - ); - } - } - } else { final JdbcMapping jdbcMapping; if ( domainParamBinding.getType() instanceof JdbcMapping ) { @@ -377,7 +362,6 @@ else if ( domainParamBinding.getBindType() instanceof BasicValuedMapping ) { final BasicValueConverter valueConverter = jdbcMapping == null ? null : jdbcMapping.getValueConverter(); if ( valueConverter != null ) { final Object convertedValue = valueConverter.toRelationalValue( domainParamBinding.getBindValue() ); - for ( int i = 0; i < jdbcParamsBinds.size(); i++ ) { final JdbcParametersList jdbcParams = jdbcParamsBinds.get( i ); assert jdbcParams.size() == 1; @@ -387,23 +371,37 @@ else if ( domainParamBinding.getBindType() instanceof BasicValuedMapping ) { new JdbcParameterBindingImpl( jdbcMapping, convertedValue ) ); } - - continue; } + else { + final Object bindValue = domainParamBinding.getBindValue(); + if ( bindValue == null ) { + for ( int i = 0; i < jdbcParamsBinds.size(); i++ ) { + final JdbcParametersList jdbcParams = jdbcParamsBinds.get( i ); + for ( int j = 0; j < jdbcParams.size(); j++ ) { + final JdbcParameter jdbcParameter = jdbcParams.get( j ); + jdbcParameterBindings.addBinding( + jdbcParameter, + new JdbcParameterBindingImpl( jdbcMapping, bindValue ) + ); + } + } + } + else { - final Object bindValue = domainParamBinding.getBindValue(); - for ( int i = 0; i < jdbcParamsBinds.size(); i++ ) { - final JdbcParametersList jdbcParams = jdbcParamsBinds.get( i ); - createValueBindings( - jdbcParameterBindings, - queryParam, - domainParamBinding, - parameterType, - jdbcParams, - bindValue, - tableGroupLocator, - session - ); + for ( int i = 0; i < jdbcParamsBinds.size(); i++ ) { + final JdbcParametersList jdbcParams = jdbcParamsBinds.get( i ); + createValueBindings( + jdbcParameterBindings, + queryParam, + domainParamBinding, + parameterType, + jdbcParams, + bindValue, + tableGroupLocator, + session + ); + } + } } } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcParameter.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcParameter.java index 902699e06456..3507d4726afb 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcParameter.java @@ -6,11 +6,11 @@ */ package org.hibernate.sql.exec.internal; -import java.io.Serializable; import java.sql.PreparedStatement; import java.sql.SQLException; import org.hibernate.cache.MutableCacheKeyBuilder; +import org.hibernate.engine.internal.CacheHelper; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.IndexedConsumer; import org.hibernate.metamodel.mapping.BasicValuedMapping; @@ -29,7 +29,6 @@ import org.hibernate.sql.exec.spi.JdbcParameterBinding; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.results.internal.SqlSelectionImpl; -import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; import org.hibernate.type.descriptor.java.EnumJavaType; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.jdbc.JdbcType; @@ -188,27 +187,7 @@ public Object disassemble(Object value, SharedSessionContractImplementor session @Override public void addToCacheKey(MutableCacheKeyBuilder cacheKey, Object value, SharedSessionContractImplementor session) { - if ( value == null ) { - return; - } - final JdbcMapping jdbcMapping = getJdbcMapping(); - final BasicValueConverter converter = jdbcMapping.getValueConverter(); - - final Serializable disassemble; - final int hashCode; - if ( converter == null ) { - final JavaType javaTypeDescriptor = jdbcMapping.getJavaTypeDescriptor(); - disassemble = javaTypeDescriptor.getMutabilityPlan().disassemble( value, session ); - hashCode = javaTypeDescriptor.extractHashCode( value ); - } - else { - final Object relationalValue = converter.toRelationalValue( value ); - final JavaType relationalJavaType = converter.getRelationalJavaType(); - disassemble = relationalJavaType.getMutabilityPlan().disassemble( relationalValue, session ); - hashCode = relationalJavaType.extractHashCode( relationalValue ); - } - cacheKey.addValue( disassemble ); - cacheKey.addHashCode( hashCode ); + CacheHelper.addBasicValueToCacheKey( cacheKey, value, getJdbcMapping(), session ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcParameterBindingsImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcParameterBindingsImpl.java index 2c8ad9059092..7d2855208a16 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcParameterBindingsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcParameterBindingsImpl.java @@ -16,7 +16,6 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.BasicValuedMapping; -import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.query.BindableType; import org.hibernate.query.spi.QueryParameterBinding; @@ -94,7 +93,7 @@ else if ( type instanceof BasicValuedMapping ) { for ( Object bindValue : bindValues ) { final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping ); jdbcParameterBinders.add( jdbcParameter ); - lastBindValue = bindValue == null ? null : valueConverter.toRelationalValue( bindValue ); + lastBindValue = valueConverter.toRelationalValue( bindValue ); addBinding( jdbcParameter, new JdbcParameterBindingImpl( jdbcMapping, lastBindValue ) ); } if ( bindValueMaxCount != bindValueCount ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/CustomType.java b/hibernate-core/src/main/java/org/hibernate/type/CustomType.java index d02a4b2e4f33..768937aad96f 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/CustomType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/CustomType.java @@ -16,6 +16,7 @@ import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.cache.MutableCacheKeyBuilder; +import org.hibernate.engine.internal.CacheHelper; import org.hibernate.engine.spi.Mapping; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -205,7 +206,7 @@ private Serializable disassembleForCache(Object value) { // we have to handle the fact that it could produce a null value, // in which case we will try to use a converter for disassembling, // or if that doesn't exist, simply use the domain value as is - if ( disassembled == null && value != null ) { + if ( disassembled == null ){ final BasicValueConverter valueConverter = getUserType().getValueConverter(); if ( valueConverter == null ) { return disassembled; @@ -220,7 +221,6 @@ private Serializable disassembleForCache(Object value) { @Override public Object disassemble(Object value, SharedSessionContractImplementor session) { // Use the value converter if available for conversion to the jdbc representation - if ( value != null ) { final BasicValueConverter valueConverter = getUserType().getValueConverter(); if ( valueConverter == null ) { return value; @@ -228,38 +228,28 @@ public Object disassemble(Object value, SharedSessionContractImplementor session else { return valueConverter.toRelationalValue( (J) value ); } - } - return value; } @Override public void addToCacheKey(MutableCacheKeyBuilder cacheKey, Object value, SharedSessionContractImplementor session) { - if ( value == null ) { - return; - } + final Serializable disassembled = getUserType().disassemble( (J) value ); // Since UserType#disassemble is an optional operation, // we have to handle the fact that it could produce a null value, // in which case we will try to use a converter for disassembling, // or if that doesn't exist, simply use the domain value as is - if ( disassembled == null && value != null ) { - final BasicValueConverter valueConverter = getUserType().getValueConverter(); - if ( valueConverter == null ) { - cacheKey.addValue( value ); - } - else { - cacheKey.addValue( - valueConverter.getRelationalJavaType().getMutabilityPlan().disassemble( - valueConverter.toRelationalValue( (J) value ), - session - ) - ); - } + if ( disassembled == null) { + CacheHelper.addBasicValueToCacheKey( cacheKey, value, this, session ); } else { cacheKey.addValue( disassembled ); + if ( value == null ) { + cacheKey.addHashCode( 0 ); + } + else { + cacheKey.addHashCode( getUserType().hashCode( (J) value ) ); + } } - cacheKey.addHashCode( getUserType().hashCode( (J) value ) ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/spi/BasicValueConverter.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/spi/BasicValueConverter.java index 718a47d8caa2..a9b43d329a82 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/spi/BasicValueConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/spi/BasicValueConverter.java @@ -11,6 +11,8 @@ import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Support for {@linkplain org.hibernate.type basic-typed} value conversions. *

@@ -30,13 +32,13 @@ public interface BasicValueConverter { * Convert the relational form just retrieved from JDBC ResultSet into * the domain form. */ - D toDomainValue(R relationalForm); + @Nullable D toDomainValue(@Nullable R relationalForm); /** * Convert the domain form into the relational form in preparation for * storage into JDBC */ - R toRelationalValue(D domainForm); + @Nullable R toRelationalValue(@Nullable D domainForm); /** * Descriptor for the Java type for the domain portion of this converter From 7be2362f295b6a2df55382e813aaab9d69997ca9 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 1 Feb 2024 11:39:07 +0100 Subject: [PATCH 092/321] HHH-17320 Add test for issue --- .../annotations/basic/SetAsBasicTest.java | 87 +++++++++++++++++++ .../basic/TreeMapAsBasicTypeTest.java | 83 ++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/basic/SetAsBasicTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/basic/TreeMapAsBasicTypeTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/basic/SetAsBasicTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/basic/SetAsBasicTest.java new file mode 100644 index 000000000000..e966181b6f14 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/basic/SetAsBasicTest.java @@ -0,0 +1,87 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.orm.test.annotations.basic; + +import java.util.HashSet; +import java.util.Set; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@DomainModel( + annotatedClasses = { + SetAsBasicTest.Post.class + } +) +@SessionFactory +public class SetAsBasicTest { + + @Test + public void testPersist(SessionFactoryScope scope) { + Integer postId = 1; + scope.inTransaction( + session -> { + Set tags = new HashSet<>(); + tags.add( "tag1" ); + + Post post = new Post( postId, "post", tags ); + session.persist( post ); + } + ); + + scope.inTransaction( + session -> { + Post post = session.find( Post.class, postId ); + Set tags = post.getTags(); + assertThat( tags ).isNotNull(); + assertThat( tags.size() ).isEqualTo( 1 ); + assertThat( tags.stream().findFirst().get() ).isEqualTo( "tag1" ); + } + ); + } + + @Entity + @Table(name = "post") + public static class Post { + @Id + public Integer id; + + public String name; + + Set tags; + + public Post() { + } + + public Post(Integer id, String name, Set tags) { + this.id = id; + this.name = name; + this.tags = tags; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public Set getTags() { + return tags; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/basic/TreeMapAsBasicTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/basic/TreeMapAsBasicTypeTest.java new file mode 100644 index 000000000000..6951cc807291 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/basic/TreeMapAsBasicTypeTest.java @@ -0,0 +1,83 @@ +package org.hibernate.orm.test.annotations.basic; + +import java.util.TreeMap; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@JiraKey("HHH-17320") +@DomainModel( + annotatedClasses = { + TreeMapAsBasicTypeTest.Customer.class + } +) +@SessionFactory +public class TreeMapAsBasicTypeTest { + + @Test + public void testPersist(SessionFactoryScope scope) { + Long cutomerId = 1l; + scope.inTransaction( + session -> { + TreeMap data = new TreeMap<>(); + data.put( "key", 1 ); + TreeMap data2 = new TreeMap<>(); + data2.put( "key2", "2" ); + Customer c = new Customer( cutomerId, data, data2 ); + session.persist( c ); + } + ); + + scope.inTransaction( + session -> { + Customer customer = session.find( Customer.class, cutomerId ); + TreeMap data = customer.getData(); + assertThat( data ).isNotNull(); + assertThat( data.get( "key" ) ).isEqualTo( 1 ); + TreeMap data2 = customer.getData2(); + assertThat( data2 ).isNotNull(); + assertThat( data2.get( "key2" ) ).isEqualTo( "2" ); + } + ); + } + + @Entity(name = "Customer") + public static class Customer { + + @Id + private Long id; + + private TreeMap data; + + private TreeMap data2; + + public Customer() { + } + + public Customer(Long id, TreeMap data, TreeMap data2) { + this.id = id; + this.data = data; + this.data2 = data2; + } + + public Long getId() { + return id; + } + + public TreeMap getData() { + return data; + } + + public TreeMap getData2() { + return data2; + } + } +} From 5ae21a10de0280b06e15162cd49c1ee5c22e1178 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 1 Feb 2024 13:01:46 +0100 Subject: [PATCH 093/321] HHH-17320 A basic attribute of type TreeMap causes a JdbcTypeRecommendationException --- .../descriptor/java/spi/JavaTypeBaseline.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeBaseline.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeBaseline.java index d247013953c0..68cf0c783600 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeBaseline.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeBaseline.java @@ -7,26 +7,17 @@ package org.hibernate.type.descriptor.java.spi; import java.lang.reflect.Type; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; -import java.util.TreeMap; -import java.util.TreeSet; import org.hibernate.collection.internal.StandardArraySemantics; import org.hibernate.collection.internal.StandardBagSemantics; import org.hibernate.collection.internal.StandardListSemantics; import org.hibernate.collection.internal.StandardMapSemantics; -import org.hibernate.collection.internal.StandardOrderedMapSemantics; -import org.hibernate.collection.internal.StandardOrderedSetSemantics; import org.hibernate.collection.internal.StandardSetSemantics; import org.hibernate.collection.internal.StandardSortedMapSemantics; import org.hibernate.collection.internal.StandardSortedSetSemantics; @@ -171,17 +162,10 @@ private static void registerCollectionTypes(BaselineTarget target) { target.addBaselineDescriptor( new CollectionJavaType( Collection.class, StandardBagSemantics.INSTANCE ) ); target.addBaselineDescriptor( new CollectionJavaType( Object[].class, StandardArraySemantics.INSTANCE ) ); target.addBaselineDescriptor( new CollectionJavaType( List.class, StandardListSemantics.INSTANCE ) ); - target.addBaselineDescriptor( new CollectionJavaType( ArrayList.class, StandardListSemantics.INSTANCE ) ); target.addBaselineDescriptor( new CollectionJavaType( Set.class, StandardSetSemantics.INSTANCE ) ); - target.addBaselineDescriptor( new CollectionJavaType( HashSet.class, StandardSetSemantics.INSTANCE ) ); target.addBaselineDescriptor( new CollectionJavaType( SortedSet.class, StandardSortedSetSemantics.INSTANCE ) ); - target.addBaselineDescriptor( new CollectionJavaType( TreeSet.class, StandardOrderedSetSemantics.INSTANCE ) ); - target.addBaselineDescriptor( new CollectionJavaType( LinkedHashSet.class, StandardOrderedSetSemantics.INSTANCE ) ); target.addBaselineDescriptor( new CollectionJavaType( Map.class, StandardMapSemantics.INSTANCE ) ); - target.addBaselineDescriptor( new CollectionJavaType( HashMap.class, StandardMapSemantics.INSTANCE ) ); target.addBaselineDescriptor( new CollectionJavaType( SortedMap.class, StandardSortedMapSemantics.INSTANCE ) ); - target.addBaselineDescriptor( new CollectionJavaType( TreeMap.class, StandardSortedMapSemantics.INSTANCE ) ); - target.addBaselineDescriptor( new CollectionJavaType( LinkedHashMap.class, StandardOrderedMapSemantics.INSTANCE ) ); } private static void primePrimitive(BaselineTarget target, JavaType descriptor) { From 722c213580379669745f15acdc4be5086f16a60c Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Fri, 2 Feb 2024 09:13:11 +0000 Subject: [PATCH 094/321] Pre-steps for release : `6.4.3.Final` --- changelog.txt | 42 +++++++++++++++++++++++++++++++++++++++ gradle/version.properties | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index d2f6e9aafd98..54cbcf3a7197 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,48 @@ Hibernate 6 Changelog Note: Please refer to JIRA to learn more about each issue. +Changes in 6.4.3.Final (February 02, 2024) +------------------------------------------------------------------------------------------------------------------------ + +https://hibernate.atlassian.net/projects/HHH/versions/32243 + +** Bug + * [HHH-17690] - Joins with Predicates using Criteria API in combination with EntityGraph poducing additional joins. + * [HHH-17689] - Cache SQL statement for unique key lookup + * [HHH-17687] - AttributeConverter, query does not use converter to convert 'null' fields + * [HHH-17686] - Join with inheritance subtype association doesn't trigger auto-flush + * [HHH-17683] - hibernate-jpamodelgen generates wrong constructor for metamodel of entities whose methods contain a `static EntityManager getEntityManager()` + * [HHH-17681] - Restore AbstractSqmSelfRenderingFunctionDescriptor backwards compatibility + * [HHH-17679] - Unnecessary entity name usage leads to collection element join + * [HHH-17677] - COALESCE usage in SQL is broken when giving NULL as second parameter + * [HHH-17674] - NullPointerException thrown when loading entity previously evicted and proxied + * [HHH-17670] - NPE in FromClause#findTableGroup + * [HHH-17668] - NullPointerException when refreshing bytecode-enhanced entity from second-level cache + * [HHH-17667] - Delete by type query with @DiscriminatorColumn generates SQL with a missing join + * [HHH-17666] - Datetime truncation fails when argument is a converted property + * [HHH-17665] - java.lang.ClassCastException in some cases when loading child entities, mapped as joined-subclass. + * [HHH-17653] - Error in generating schema when @Generator annotation is applied to a non id embeddable property + * [HHH-17644] - Mapping of generic types in single table inheritance depends on lexicographical order of parent and child classnames + * [HHH-17643] - Allow uninitialized proxy serialization even when a SessionFactory is not available + * [HHH-17634] - Merging a new entity having a @GeneratedValue id should not set the generated id to the original entity + * [HHH-17629] - Criteria and Entity graph generates same join clause twice + * [HHH-17605] - NativeQuery - Multiple entities of same type are translated into the same single object + * [HHH-17598] - Criteria multiselect doesn't work properly for array types other than Object[] + * [HHH-17594] - "Could not set value of type" for @EmbeddedId that uses a @DiscriminatorValue as Key + * [HHH-17587] - Setting to null a property from a @SecondaryTable and @DynamicUpdate deletes the whole entry from database + * [HHH-17550] - NO_CONSTRAINT not honored by InheritanceType.JOINED + * [HHH-17526] - During eager batch fetching, real exceptions swallowed by exception in finally + * [HHH-17461] - Add explicit @SoftDelete column duplication validation + * [HHH-17420] - JoinColumn throws an `occurs out of order` AnnotationException + * [HHH-17326] - deprecated lazyLoading no longer working + * [HHH-17320] - A basic attribute of type TreeMap causes a JdbcTypeRecommendationException + * [HHH-17113] - Incorrect SQL generated on DiscriminatorColumn when combined with bidirectional relation and @DiscriminatorOptions(force = true) + * [HHH-16960] - OneToOne lazy loading fails when fetch graph is involved + +** Improvement + * [HHH-17319] - Use Oracle GraalVM for Atlas builds + + Changes in 6.4.2.Final (January 18, 2024) ------------------------------------------------------------------------------------------------------------------------ diff --git a/gradle/version.properties b/gradle/version.properties index ef2f718af356..42f0c59a543d 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -hibernateVersion=6.4.3-SNAPSHOT \ No newline at end of file +hibernateVersion=6.4.3.Final \ No newline at end of file From ad5e3dfbc08d897d82845cd2fbeccfe0a562208a Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Fri, 2 Feb 2024 09:13:24 +0000 Subject: [PATCH 095/321] Post-steps for release : `6.4.3.Final` --- gradle/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/version.properties b/gradle/version.properties index 42f0c59a543d..f77490443d1c 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -hibernateVersion=6.4.3.Final \ No newline at end of file +hibernateVersion=6.4.4-SNAPSHOT \ No newline at end of file From 439fac712be53f82a702a557815a6b20e48b2af5 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Fri, 2 Feb 2024 09:38:10 +0100 Subject: [PATCH 096/321] Fix ORA-21700 in SetAsBasicTest --- .../hibernate/orm/test/annotations/basic/SetAsBasicTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/basic/SetAsBasicTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/basic/SetAsBasicTest.java index e966181b6f14..6e628f143d85 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/basic/SetAsBasicTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/basic/SetAsBasicTest.java @@ -9,6 +9,8 @@ import java.util.HashSet; import java.util.Set; +import org.hibernate.testing.jdbc.SharedDriverManagerTypeCacheClearingIntegrator; +import org.hibernate.testing.orm.junit.BootstrapServiceRegistry; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; @@ -26,6 +28,8 @@ } ) @SessionFactory +// Clear the type cache, otherwise we might run into ORA-21700: object does not exist or is marked for delete +@BootstrapServiceRegistry(integrators = SharedDriverManagerTypeCacheClearingIntegrator.class) public class SetAsBasicTest { @Test From 5d348fc723781a2318648ebd20276f9211a61dfd Mon Sep 17 00:00:00 2001 From: Stephanie Miller Date: Tue, 23 Jan 2024 12:18:32 -0500 Subject: [PATCH 097/321] HHH-17662 Equals for ArrayJdbcType JdbcTypes are put into a map and deduplicated there. Without an equals the ArrayJdbcType leaks because each resolution is created new. --- .../type/descriptor/jdbc/ArrayJdbcType.java | 18 +++++++++++++ .../descriptor/jdbc/ArrayJdbcTypeTest.java | 27 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/type/descriptor/jdbc/ArrayJdbcTypeTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java index 555447b6abf9..f83ebd131e05 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java @@ -203,4 +203,22 @@ public String getFriendlyName() { public String toString() { return "ArrayTypeDescriptor"; } + + /** + * Check equality. Needed so that ArrayJdbcType in collections correctly match each other. + * + * @param o other object + * @return true if the two array types share the same element type + */ + @Override + public boolean equals(Object o) { + return o != null && + getClass() == o.getClass() && + getElementJdbcType().equals( ((ArrayJdbcType) o).getElementJdbcType() ); + } + + @Override + public int hashCode() { + return getJdbcTypeCode() + getElementJdbcType().hashCode(); + } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/type/descriptor/jdbc/ArrayJdbcTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/descriptor/jdbc/ArrayJdbcTypeTest.java new file mode 100644 index 000000000000..22d5a3125ed3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/descriptor/jdbc/ArrayJdbcTypeTest.java @@ -0,0 +1,27 @@ +package org.hibernate.orm.test.type.descriptor.jdbc; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.type.descriptor.jdbc.ArrayJdbcType; +import org.hibernate.type.descriptor.jdbc.BigIntJdbcType; +import org.hibernate.type.descriptor.jdbc.IntegerJdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.hamcrest.MatcherAssert.assertThat; + +public class ArrayJdbcTypeTest { + @Test + @TestForIssue(jiraKey = "HHH-17662") + public void testEquality() { + Map typeMap = new HashMap<>(); + JdbcType bigInt = new BigIntJdbcType(); + typeMap.put(new ArrayJdbcType(bigInt), "bees"); + typeMap.put(new ArrayJdbcType(bigInt), "bees"); + typeMap.put(new ArrayJdbcType(bigInt), "bees"); + typeMap.put(new ArrayJdbcType(new IntegerJdbcType()), "waffles"); + assertThat("A map of arrays only contains non duplicate entries", typeMap.size() == 2); + } +} From 2661e5cd188ed6a6d86e7ae436d2a943a5cea2ed Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Wed, 24 Jan 2024 11:38:08 +0100 Subject: [PATCH 098/321] HHH-17662 Replace JdbcTypeConstructor uses for arrays with uniform resolve method --- .../dialect/CockroachLegacyDialect.java | 8 +- .../dialect/OracleLegacyDialect.java | 15 +- .../dialect/PostgreSQLLegacyDialect.java | 8 +- .../hibernate/dialect/CockroachDialect.java | 8 +- .../java/org/hibernate/dialect/Dialect.java | 28 ++-- .../OracleArrayJdbcTypeConstructor.java | 19 ++- .../org/hibernate/dialect/OracleDialect.java | 14 +- .../OracleNestedTableJdbcTypeConstructor.java | 19 ++- .../hibernate/dialect/PostgreSQLDialect.java | 8 +- .../java/AbstractArrayJavaType.java | 63 +------ .../java/spi/BasicCollectionJavaType.java | 52 +----- .../type/descriptor/jdbc/ArrayJdbcType.java | 4 +- .../descriptor/jdbc/spi/JdbcTypeRegistry.java | 154 ++++++++++++++++++ 13 files changed, 228 insertions(+), 172 deletions(-) diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java index e6045033cbf7..6362e3a91b7e 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java @@ -300,15 +300,13 @@ public JdbcType resolveSqlTypeDescriptor( } break; case ARRAY: - final JdbcTypeConstructor jdbcTypeConstructor = jdbcTypeRegistry.getConstructor( jdbcTypeCode ); // PostgreSQL names array types by prepending an underscore to the base name - if ( jdbcTypeConstructor != null && columnTypeName.charAt( 0 ) == '_' ) { + if ( columnTypeName.charAt( 0 ) == '_' ) { final String componentTypeName = columnTypeName.substring( 1 ); final Integer sqlTypeCode = resolveSqlTypeCode( componentTypeName, jdbcTypeRegistry.getTypeConfiguration() ); if ( sqlTypeCode != null ) { - return jdbcTypeConstructor.resolveType( - jdbcTypeRegistry.getTypeConfiguration(), - this, + return jdbcTypeRegistry.resolveTypeConstructorDescriptor( + jdbcTypeCode, jdbcTypeRegistry.getDescriptor( sqlTypeCode ), ColumnTypeInformation.EMPTY ); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java index 940dc94b8128..d88fe3fc2379 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java @@ -100,7 +100,6 @@ import org.hibernate.type.descriptor.jdbc.AggregateJdbcType; import org.hibernate.type.descriptor.jdbc.BlobJdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType; -import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor; import org.hibernate.type.descriptor.jdbc.OracleJsonBlobJdbcType; import org.hibernate.type.descriptor.jdbc.NullJdbcType; import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcType; @@ -760,15 +759,11 @@ public JdbcType resolveSqlTypeDescriptor( break; case ARRAY: if ( "MDSYS.SDO_ORDINATE_ARRAY".equals( columnTypeName ) ) { - final JdbcTypeConstructor jdbcTypeConstructor = jdbcTypeRegistry.getConstructor( jdbcTypeCode ); - if ( jdbcTypeConstructor != null ) { - return jdbcTypeConstructor.resolveType( - jdbcTypeRegistry.getTypeConfiguration(), - this, - jdbcTypeRegistry.getDescriptor( NUMERIC ), - ColumnTypeInformation.EMPTY - ); - } + return jdbcTypeRegistry.resolveTypeConstructorDescriptor( + jdbcTypeCode, + jdbcTypeRegistry.getDescriptor( NUMERIC ), + ColumnTypeInformation.EMPTY + ); } break; case Types.NUMERIC: diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java index 502de8521c4b..f41b96733782 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java @@ -328,15 +328,13 @@ public JdbcType resolveSqlTypeDescriptor( } break; case ARRAY: - final JdbcTypeConstructor jdbcTypeConstructor = jdbcTypeRegistry.getConstructor( jdbcTypeCode ); // PostgreSQL names array types by prepending an underscore to the base name - if ( jdbcTypeConstructor != null && columnTypeName.charAt( 0 ) == '_' ) { + if ( columnTypeName.charAt( 0 ) == '_' ) { final String componentTypeName = columnTypeName.substring( 1 ); final Integer sqlTypeCode = resolveSqlTypeCode( componentTypeName, jdbcTypeRegistry.getTypeConfiguration() ); if ( sqlTypeCode != null ) { - return jdbcTypeConstructor.resolveType( - jdbcTypeRegistry.getTypeConfiguration(), - this, + return jdbcTypeRegistry.resolveTypeConstructorDescriptor( + jdbcTypeCode, jdbcTypeRegistry.getDescriptor( sqlTypeCode ), ColumnTypeInformation.EMPTY ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java index 76490fb2bb92..1dd0b09c3fa0 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -318,15 +318,13 @@ public JdbcType resolveSqlTypeDescriptor( } break; case ARRAY: - final JdbcTypeConstructor jdbcTypeConstructor = jdbcTypeRegistry.getConstructor( jdbcTypeCode ); // PostgreSQL names array types by prepending an underscore to the base name - if ( jdbcTypeConstructor != null && columnTypeName.charAt( 0 ) == '_' ) { + if ( columnTypeName.charAt( 0 ) == '_' ) { final String componentTypeName = columnTypeName.substring( 1 ); final Integer sqlTypeCode = resolveSqlTypeCode( componentTypeName, jdbcTypeRegistry.getTypeConfiguration() ); if ( sqlTypeCode != null ) { - return jdbcTypeConstructor.resolveType( - jdbcTypeRegistry.getTypeConfiguration(), - this, + return jdbcTypeRegistry.resolveTypeConstructorDescriptor( + jdbcTypeCode, jdbcTypeRegistry.getDescriptor( sqlTypeCode ), ColumnTypeInformation.EMPTY ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 82d74c52eece..4d041cd6ba1b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -725,22 +725,18 @@ public JdbcType resolveSqlTypeDescriptor( int scale, JdbcTypeRegistry jdbcTypeRegistry) { if ( jdbcTypeCode == ARRAY ) { - final JdbcTypeConstructor jdbcTypeConstructor = jdbcTypeRegistry.getConstructor( jdbcTypeCode ); - if ( jdbcTypeConstructor != null ) { - // Special handling for array types, because we need the proper element/component type - // To determine the element JdbcType, we pass the database reported type to #resolveSqlTypeCode - final int arraySuffixIndex = columnTypeName.toLowerCase( Locale.ROOT ).indexOf( " array" ); - if ( arraySuffixIndex != -1 ) { - final String componentTypeName = columnTypeName.substring( 0, arraySuffixIndex ); - final Integer sqlTypeCode = resolveSqlTypeCode( componentTypeName, jdbcTypeRegistry.getTypeConfiguration() ); - if ( sqlTypeCode != null ) { - return jdbcTypeConstructor.resolveType( - jdbcTypeRegistry.getTypeConfiguration(), - this, - jdbcTypeRegistry.getDescriptor( sqlTypeCode ), - ColumnTypeInformation.EMPTY - ); - } + // Special handling for array types, because we need the proper element/component type + // To determine the element JdbcType, we pass the database reported type to #resolveSqlTypeCode + final int arraySuffixIndex = columnTypeName.toLowerCase( Locale.ROOT ).indexOf( " array" ); + if ( arraySuffixIndex != -1 ) { + final String componentTypeName = columnTypeName.substring( 0, arraySuffixIndex ); + final Integer sqlTypeCode = resolveSqlTypeCode( componentTypeName, jdbcTypeRegistry.getTypeConfiguration() ); + if ( sqlTypeCode != null ) { + return jdbcTypeRegistry.resolveTypeConstructorDescriptor( + jdbcTypeCode, + jdbcTypeRegistry.getDescriptor( sqlTypeCode ), + ColumnTypeInformation.EMPTY + ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleArrayJdbcTypeConstructor.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleArrayJdbcTypeConstructor.java index 280ded56d173..4818cac81b8b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleArrayJdbcTypeConstructor.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleArrayJdbcTypeConstructor.java @@ -46,10 +46,21 @@ public JdbcType resolveType( JdbcType elementType, ColumnTypeInformation columnTypeInformation) { // a bit wrong, since columnTypeInformation.getTypeName() is typically null! - return new OracleArrayJdbcType( - elementType, - columnTypeInformation == null ? null : columnTypeInformation.getTypeName() - ); + String typeName = columnTypeInformation == null ? null : columnTypeInformation.getTypeName(); + if ( typeName == null || typeName.isBlank() ) { + Integer precision = null; + Integer scale = null; + if ( columnTypeInformation != null ) { + precision = columnTypeInformation.getColumnSize(); + scale = columnTypeInformation.getDecimalDigits(); + } + typeName = OracleArrayJdbcType.getTypeName( elementType.getJdbcRecommendedJavaTypeMapping( + precision, + scale, + typeConfiguration + ), dialect ); + } + return new OracleArrayJdbcType( elementType, typeName ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index 44c7236ee20a..6b45b68ae1e3 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -799,15 +799,11 @@ public JdbcType resolveSqlTypeDescriptor( break; case ARRAY: if ( "MDSYS.SDO_ORDINATE_ARRAY".equals( columnTypeName ) ) { - final JdbcTypeConstructor jdbcTypeConstructor = jdbcTypeRegistry.getConstructor( jdbcTypeCode ); - if ( jdbcTypeConstructor != null ) { - return jdbcTypeConstructor.resolveType( - jdbcTypeRegistry.getTypeConfiguration(), - this, - jdbcTypeRegistry.getDescriptor( NUMERIC ), - ColumnTypeInformation.EMPTY - ); - } + return jdbcTypeRegistry.resolveTypeConstructorDescriptor( + jdbcTypeCode, + jdbcTypeRegistry.getDescriptor( NUMERIC ), + ColumnTypeInformation.EMPTY + ); } break; case NUMERIC: diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleNestedTableJdbcTypeConstructor.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleNestedTableJdbcTypeConstructor.java index cc148c2b3f84..7199ec1b3236 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleNestedTableJdbcTypeConstructor.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleNestedTableJdbcTypeConstructor.java @@ -40,10 +40,21 @@ public JdbcType resolveType( JdbcType elementType, ColumnTypeInformation columnTypeInformation) { // a bit wrong, since columnTypeInformation.getTypeName() is typically null! - return new OracleNestedTableJdbcType( - elementType, - columnTypeInformation == null ? null : columnTypeInformation.getTypeName() - ); + String typeName = columnTypeInformation == null ? null : columnTypeInformation.getTypeName(); + if ( typeName == null || typeName.isBlank() ) { + Integer precision = null; + Integer scale = null; + if ( columnTypeInformation != null ) { + precision = columnTypeInformation.getColumnSize(); + scale = columnTypeInformation.getDecimalDigits(); + } + typeName = OracleArrayJdbcType.getTypeName( elementType.getJdbcRecommendedJavaTypeMapping( + precision, + scale, + typeConfiguration + ), dialect ); + } + return new OracleNestedTableJdbcType( elementType, typeName ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index 7cabad34affc..2804ca1ecd3d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -347,15 +347,13 @@ public JdbcType resolveSqlTypeDescriptor( } break; case ARRAY: - final JdbcTypeConstructor jdbcTypeConstructor = jdbcTypeRegistry.getConstructor( jdbcTypeCode ); // PostgreSQL names array types by prepending an underscore to the base name - if ( jdbcTypeConstructor != null && columnTypeName.charAt( 0 ) == '_' ) { + if ( columnTypeName.charAt( 0 ) == '_' ) { final String componentTypeName = columnTypeName.substring( 1 ); final Integer sqlTypeCode = resolveSqlTypeCode( componentTypeName, jdbcTypeRegistry.getTypeConfiguration() ); if ( sqlTypeCode != null ) { - return jdbcTypeConstructor.resolveType( - jdbcTypeRegistry.getTypeConfiguration(), - this, + return jdbcTypeRegistry.resolveTypeConstructorDescriptor( + jdbcTypeCode, jdbcTypeRegistry.getDescriptor( sqlTypeCode ), ColumnTypeInformation.EMPTY ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractArrayJavaType.java index 1011e5ec6f2f..74f3c19836e9 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractArrayJavaType.java @@ -17,9 +17,7 @@ import org.hibernate.type.ConvertedBasicArrayType; import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; import org.hibernate.type.descriptor.jdbc.JdbcType; -import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; -import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.type.internal.BasicTypeImpl; import org.hibernate.type.spi.TypeConfiguration; @@ -41,11 +39,9 @@ public JavaType getElementJavaType() { @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { // Always determine the recommended type to make sure this is a valid basic java type - return getArrayJdbcType( - indicators.getTypeConfiguration(), - indicators.getDialect(), + return indicators.getTypeConfiguration().getJdbcTypeRegistry().resolveTypeConstructorDescriptor( indicators.getPreferredSqlTypeCodeForArray(), - new BasicTypeImpl<>( getElementJavaType(), componentJavaType.getRecommendedJdbcType( indicators ) ), + new BasicTypeImpl<>( componentJavaType, componentJavaType.getRecommendedJdbcType( indicators ) ), ColumnTypeInformation.EMPTY ); } @@ -86,9 +82,7 @@ BasicType createTypeUsingConverter( final JavaType relationalJavaType = typeConfiguration.getJavaTypeRegistry().getDescriptor( convertedArrayClass ); return new ConvertedBasicArrayType<>( elementType, - getArrayJdbcType( - typeConfiguration, - dialect, + typeConfiguration.getJdbcTypeRegistry().resolveTypeConstructorDescriptor( stdIndicators.getExplicitJdbcTypeCode(), elementType, columnTypeInformation @@ -105,9 +99,7 @@ BasicType resolveType( BasicType elementType, ColumnTypeInformation columnTypeInformation, JdbcTypeIndicators stdIndicators) { - final JdbcType arrayJdbcType = getArrayJdbcType( - typeConfiguration, - dialect, + final JdbcType arrayJdbcType = typeConfiguration.getJdbcTypeRegistry().resolveTypeConstructorDescriptor( stdIndicators.getExplicitJdbcTypeCode(), elementType, columnTypeInformation @@ -117,53 +109,6 @@ BasicType resolveType( arrayJdbcType, () -> new BasicArrayType<>( elementType, arrayJdbcType, arrayJavaType ) ); -// return typeConfiguration.getBasicTypeRegistry().getRegisteredType( elementType.getName() ) == elementType -// ? typeConfiguration.standardBasicTypeForJavaType( -// arrayJavaType.getJavaType(), -// javaType -> basicArrayType( typeConfiguration, dialect, elementType, columnTypeInformation, stdIndicators, arrayJavaType ) -// ) -// : basicArrayType( typeConfiguration, dialect, elementType, columnTypeInformation, stdIndicators, arrayJavaType ); } -// BasicType basicArrayType( -// TypeConfiguration typeConfiguration, -// Dialect dialect, -// BasicType elementType, -// ColumnTypeInformation columnTypeInformation, -// JdbcTypeIndicators stdIndicators, -// JavaType javaType) { -// return new BasicArrayType<>( -// elementType, -// getArrayJdbcType( -// typeConfiguration, -// dialect, -// stdIndicators.getExplicitJdbcTypeCode(), -// elementType, -// columnTypeInformation -// ), -// javaType -// ); -// } - - static JdbcType getArrayJdbcType( - TypeConfiguration typeConfiguration, - Dialect dialect, - int preferredSqlTypeCodeForArray, - BasicType elementType, - ColumnTypeInformation columnTypeInformation) { - final JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry(); - final JdbcTypeConstructor arrayJdbcTypeConstructor = - jdbcTypeRegistry.getConstructor( preferredSqlTypeCodeForArray ); - if ( arrayJdbcTypeConstructor != null ) { - return arrayJdbcTypeConstructor.resolveType( - typeConfiguration, - dialect, - elementType, - columnTypeInformation - ); - } - else { - return jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForArray ); - } - } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/BasicCollectionJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/BasicCollectionJavaType.java index 177d0ba5df40..74eaabfbfb3b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/BasicCollectionJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/BasicCollectionJavaType.java @@ -10,11 +10,9 @@ import java.lang.reflect.Array; import java.lang.reflect.ParameterizedType; import java.sql.SQLException; -import java.sql.Types; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; -import java.util.function.Function; import org.hibernate.HibernateException; import org.hibernate.Incubating; @@ -27,7 +25,6 @@ import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.metamodel.CollectionClassification; import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation; -import org.hibernate.type.BasicArrayType; import org.hibernate.type.BasicCollectionType; import org.hibernate.type.BasicPluralType; import org.hibernate.type.BasicType; @@ -40,9 +37,7 @@ import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.MutabilityPlan; import org.hibernate.type.descriptor.jdbc.JdbcType; -import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; -import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.type.internal.BasicTypeImpl; import org.hibernate.type.spi.TypeConfiguration; @@ -73,11 +68,9 @@ public JavaType getElementJavaType() { public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { // Always determine the recommended type to make sure this is a valid basic java type // (even though we only use this inside the if block, we want it to throw here if something wrong) - return getArrayJdbcType( - indicators.getTypeConfiguration(), - indicators.getDialect(), + return indicators.getTypeConfiguration().getJdbcTypeRegistry().resolveTypeConstructorDescriptor( indicators.getPreferredSqlTypeCodeForArray(), - new BasicTypeImpl<>( getElementJavaType(), componentJavaType.getRecommendedJdbcType( indicators ) ), + new BasicTypeImpl<>( componentJavaType, componentJavaType.getRecommendedJdbcType( indicators ) ), ColumnTypeInformation.EMPTY ); } @@ -119,23 +112,11 @@ public BasicType resolveType( } final BasicValueConverter valueConverter = elementType.getValueConverter(); if ( valueConverter == null ) { - final JdbcType arrayJdbcType = getArrayJdbcType( - typeConfiguration, - dialect, + final JdbcType arrayJdbcType = typeConfiguration.getJdbcTypeRegistry().resolveTypeConstructorDescriptor( stdIndicators.getPreferredSqlTypeCodeForArray(), elementType, columnTypeInformation ); - final Function, BasicType> creator = javaType -> { - - //noinspection unchecked,rawtypes - return new BasicCollectionType( elementType, arrayJdbcType, collectionJavaType ); - }; -// if ( typeConfiguration.getBasicTypeRegistry().getRegisteredType( elementType.getName() ) == elementType ) { -// return typeConfiguration.standardBasicTypeForJavaType( collectionJavaType.getJavaType(), creator ); -// } -// //noinspection unchecked -// return creator.apply( (JavaType) (JavaType) collectionJavaType ); return typeConfiguration.getBasicTypeRegistry().resolve( collectionJavaType, arrayJdbcType, @@ -149,9 +130,7 @@ public BasicType resolveType( //noinspection unchecked,rawtypes return new ConvertedBasicCollectionType( elementType, - getArrayJdbcType( - typeConfiguration, - dialect, + typeConfiguration.getJdbcTypeRegistry().resolveTypeConstructorDescriptor( stdIndicators.getPreferredSqlTypeCodeForArray(), elementType, columnTypeInformation @@ -162,29 +141,6 @@ public BasicType resolveType( } } - //TODO: copy/pasted from AbstractArrayJavaType - private static JdbcType getArrayJdbcType( - TypeConfiguration typeConfiguration, - Dialect dialect, - int preferredSqlTypeCodeForArray, - BasicType elementType, - ColumnTypeInformation columnTypeInformation) { - final JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry(); - final JdbcTypeConstructor arrayJdbcTypeConstructor = - jdbcTypeRegistry.getConstructor( preferredSqlTypeCodeForArray ); - if ( arrayJdbcTypeConstructor != null ) { - return arrayJdbcTypeConstructor.resolveType( - typeConfiguration, - dialect, - elementType, - columnTypeInformation - ); - } - else { - return jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForArray ); - } - } - @Override public String extractLoggableRepresentation(C value) { if ( value == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java index f83ebd131e05..1b6335d08bf7 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java @@ -214,11 +214,11 @@ public String toString() { public boolean equals(Object o) { return o != null && getClass() == o.getClass() && - getElementJdbcType().equals( ((ArrayJdbcType) o).getElementJdbcType() ); + getElementJdbcType().equals( ( (ArrayJdbcType) o ).getElementJdbcType() ); } @Override public int hashCode() { - return getJdbcTypeCode() + getElementJdbcType().hashCode(); + return getJdbcTypeCode() + 31 * getElementJdbcType().hashCode(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/spi/JdbcTypeRegistry.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/spi/JdbcTypeRegistry.java index dfba08f3a07a..cf8280bc7f02 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/spi/JdbcTypeRegistry.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/spi/JdbcTypeRegistry.java @@ -7,13 +7,20 @@ package org.hibernate.type.descriptor.jdbc.spi; import java.io.Serializable; +import java.sql.Types; import java.util.Locale; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import org.hibernate.boot.model.TruthValue; +import org.hibernate.dialect.Dialect; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; +import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation; +import org.hibernate.type.BasicType; import org.hibernate.type.descriptor.JdbcTypeNameMapper; import org.hibernate.type.descriptor.jdbc.AggregateJdbcType; +import org.hibernate.type.descriptor.jdbc.ArrayJdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor; import org.hibernate.type.descriptor.jdbc.JdbcTypeFamilyInformation; @@ -23,6 +30,8 @@ import org.jboss.logging.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A registry mapping {@link org.hibernate.type.SqlTypes JDBC type codes} * to implementations of the {@link JdbcType} interface. @@ -39,6 +48,13 @@ public class JdbcTypeRegistry implements JdbcTypeBaseline.BaselineTarget, Serial private final ConcurrentHashMap descriptorMap = new ConcurrentHashMap<>(); private final ConcurrentHashMap descriptorConstructorMap = new ConcurrentHashMap<>(); private final ConcurrentHashMap aggregateDescriptorMap = new ConcurrentHashMap<>(); + /** + * A registry for storing the constructed {@link JdbcType} for both + * {@link JdbcTypeConstructor#resolveType(TypeConfiguration, Dialect, JdbcType, ColumnTypeInformation)} and + * {@link JdbcTypeConstructor#resolveType(TypeConfiguration, Dialect, BasicType, ColumnTypeInformation)} in a single + * map. + */ + private final ConcurrentHashMap typeConstructorDescriptorMap = new ConcurrentHashMap<>(); public JdbcTypeRegistry(TypeConfiguration typeConfiguration) { this.typeConfiguration = typeConfiguration; @@ -174,6 +190,68 @@ public AggregateJdbcType findAggregateDescriptor(String typeName) { return aggregateDescriptorMap.get( typeName.toLowerCase( Locale.ROOT ) ); } + /** + * Construct a {@link JdbcType} via {@link JdbcTypeConstructor#resolveType(TypeConfiguration, Dialect, BasicType, ColumnTypeInformation)} + * or return a compatible one from this registry. + */ + public JdbcType resolveTypeConstructorDescriptor( + int jdbcTypeConstructorCode, + BasicType elementType, + @Nullable ColumnTypeInformation columnTypeInformation) { + return resolveTypeConstructorDescriptor( jdbcTypeConstructorCode, (Object) elementType, columnTypeInformation ); + } + + /** + * Construct a {@link JdbcType} via {@link JdbcTypeConstructor#resolveType(TypeConfiguration, Dialect, JdbcType, ColumnTypeInformation)} + * or return a compatible one from this registry. + */ + public JdbcType resolveTypeConstructorDescriptor( + int jdbcTypeConstructorCode, + JdbcType elementType, + @Nullable ColumnTypeInformation columnTypeInformation) { + return resolveTypeConstructorDescriptor( jdbcTypeConstructorCode, (Object) elementType, columnTypeInformation ); + } + + private JdbcType resolveTypeConstructorDescriptor( + int jdbcTypeConstructorCode, + Object elementType, + @Nullable ColumnTypeInformation columnTypeInformation) { + final TypeConstructedJdbcTypeKey key = new TypeConstructedJdbcTypeKey( + jdbcTypeConstructorCode, + elementType, + columnTypeInformation + ); + final JdbcType descriptor = typeConstructorDescriptorMap.get( key ); + if ( descriptor != null ) { + return descriptor; + } + final JdbcTypeConstructor jdbcTypeConstructor = getConstructor( jdbcTypeConstructorCode ); + if ( jdbcTypeConstructor != null ) { + final JdbcType jdbcType; + if ( elementType instanceof BasicType ) { + jdbcType = jdbcTypeConstructor.resolveType( + typeConfiguration, + typeConfiguration.getCurrentBaseSqlTypeIndicators().getDialect(), + (BasicType) elementType, + columnTypeInformation + ); + } + else { + jdbcType = jdbcTypeConstructor.resolveType( + typeConfiguration, + typeConfiguration.getCurrentBaseSqlTypeIndicators().getDialect(), + (JdbcType) elementType, + columnTypeInformation + ); + } + final JdbcType existingType = typeConstructorDescriptorMap.putIfAbsent( key, jdbcType ); + return existingType != null ? existingType : jdbcType; + } + else { + return getDescriptor( jdbcTypeConstructorCode ); + } + } + public boolean hasRegisteredDescriptor(int jdbcTypeCode) { return descriptorMap.containsKey( jdbcTypeCode ) || JdbcTypeNameMapper.isStandardTypeCode( jdbcTypeCode ) @@ -191,4 +269,80 @@ public void addTypeConstructor(int jdbcTypeCode, JdbcTypeConstructor jdbcTypeCon public void addTypeConstructor(JdbcTypeConstructor jdbcTypeConstructor) { addTypeConstructor( jdbcTypeConstructor.getDefaultSqlTypeCode(), jdbcTypeConstructor ); } + + private static final class TypeConstructedJdbcTypeKey { + private final int typeConstructorTypeCode; + private final Object jdbcTypeOrBasicType; + private final TruthValue nullable; + private final int typeCode; + private final @Nullable String typeName; + private final int columnSize; + private final int decimalDigits; + + public TypeConstructedJdbcTypeKey( + int typeConstructorTypeCode, + Object jdbcTypeOrBasicType, + @Nullable ColumnTypeInformation columnTypeInformation) { + this.typeConstructorTypeCode = typeConstructorTypeCode; + this.jdbcTypeOrBasicType = jdbcTypeOrBasicType; + if ( columnTypeInformation == null ) { + this.nullable = TruthValue.UNKNOWN; + this.typeCode = Types.OTHER; + this.typeName = null; + this.columnSize = 0; + this.decimalDigits = 0; + } + else { + this.nullable = columnTypeInformation.getNullable(); + this.typeCode = columnTypeInformation.getTypeCode(); + this.typeName = columnTypeInformation.getTypeName(); + this.columnSize = columnTypeInformation.getColumnSize(); + this.decimalDigits = columnTypeInformation.getDecimalDigits(); + } + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + TypeConstructedJdbcTypeKey that = (TypeConstructedJdbcTypeKey) o; + + if ( typeConstructorTypeCode != that.typeConstructorTypeCode ) { + return false; + } + if ( typeCode != that.typeCode ) { + return false; + } + if ( columnSize != that.columnSize ) { + return false; + } + if ( decimalDigits != that.decimalDigits ) { + return false; + } + if ( !jdbcTypeOrBasicType.equals( that.jdbcTypeOrBasicType ) ) { + return false; + } + if ( nullable != that.nullable ) { + return false; + } + return Objects.equals( typeName, that.typeName ); + } + + @Override + public int hashCode() { + int result = typeConstructorTypeCode; + result = 31 * result + jdbcTypeOrBasicType.hashCode(); + result = 31 * result + ( nullable != null ? nullable.hashCode() : 0 ); + result = 31 * result + typeCode; + result = 31 * result + ( typeName != null ? typeName.hashCode() : 0 ); + result = 31 * result + columnSize; + result = 31 * result + decimalDigits; + return result; + } + } } From 342ca85c978f64f9bdf5bc4389fa498fd600c814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 5 Feb 2024 13:16:58 +0100 Subject: [PATCH 099/321] HHH-17708 Fix formatting/anchor in documentation of @Struct --- .../userguide/chapters/domain/DomainModel.adoc | 15 +++++++++++++++ .../userguide/chapters/domain/embeddables.adoc | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/userguide/chapters/domain/DomainModel.adoc b/documentation/src/main/asciidoc/userguide/chapters/domain/DomainModel.adoc index 429577cdf587..dac10966cb7a 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/domain/DomainModel.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/domain/DomainModel.adoc @@ -23,18 +23,33 @@ adjusting these names see <>. ==== include::types.adoc[] + include::basic_types.adoc[] + include::embeddables.adoc[] + include::entity.adoc[] + include::naming.adoc[] + include::access.adoc[] + include::identifiers.adoc[] + include::associations.adoc[] + include::collections.adoc[] + include::natural_id.adoc[] + include::partitioning.adoc[] + include::soft_delete.adoc[] + include::dynamic_model.adoc[] + include::inheritance.adoc[] + include::immutability.adoc[] + include::customizing.adoc[] diff --git a/documentation/src/main/asciidoc/userguide/chapters/domain/embeddables.adoc b/documentation/src/main/asciidoc/userguide/chapters/domain/embeddables.adoc index c157d17f0783..dd8af3f81cd7 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/domain/embeddables.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/domain/embeddables.adoc @@ -611,7 +611,7 @@ create table JsonHolder as ( Again, the name and the nullability of the `aggregate` column can be refined through applying a `@Column` on the persistent attribute. -[[embeddable-mapping-aggregate]] +[[embeddable-mapping-aggregate-collections]] ==== Embeddable mappings containing collections Mapping <> inside an `@Embeddable` value is supported in most cases. There are a couple exceptions: From 0a41fa41f5514a38636e592f897ff89ac33c0af8 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Mon, 5 Feb 2024 12:31:05 +0100 Subject: [PATCH 100/321] HHH-17705 Load default bytecode provider using the correct ClassLoader --- .../bytecode/internal/BytecodeProviderInitiator.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeProviderInitiator.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeProviderInitiator.java index 4e2487540c57..002db6746064 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeProviderInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeProviderInitiator.java @@ -58,7 +58,11 @@ public Class getServiceInitiated() { @Internal public static BytecodeProvider buildDefaultBytecodeProvider() { - return getBytecodeProvider( ServiceLoader.load( BytecodeProvider.class ) ); + // Use BytecodeProvider's ClassLoader to ensure we can find the service + return getBytecodeProvider( ServiceLoader.load( + BytecodeProvider.class, + BytecodeProvider.class.getClassLoader() + ) ); } @Internal From e8793a883b4ee4cc80ee4c0a4a321514e689e1dd Mon Sep 17 00:00:00 2001 From: Scott Marlow Date: Mon, 5 Feb 2024 17:51:51 -0500 Subject: [PATCH 101/321] HHH-17713 Upgrade ByteBuddy to 1.14.11 Signed-off-by: Scott Marlow --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index d4510618b1cc..e79416d1110a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -65,7 +65,7 @@ dependencyResolutionManagement { } libs { def antlrVersion = version "antlr", "4.13.0" - def byteBuddyVersion = version "byteBuddy", "1.14.7" + def byteBuddyVersion = version "byteBuddy", "1.14.11" def classmateVersion = version "classmate", "1.5.1" def geolatteVersion = version "geolatte", "1.8.2" def hcannVersion = version "hcann", "6.0.6.Final" From 02fa42d90c18b88be4b2395284e34dcc7708aa62 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Mon, 29 Jan 2024 13:00:01 +0100 Subject: [PATCH 102/321] HHH-17688 Add test for issue --- .../MutationDelegateStatementReleaseTest.java | 278 ++++++++++++++++++ .../src/test/resources/log4j2.properties | 3 + 2 files changed, 281 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/delegate/MutationDelegateStatementReleaseTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/delegate/MutationDelegateStatementReleaseTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/delegate/MutationDelegateStatementReleaseTest.java new file mode 100644 index 000000000000..30e7280ef51d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/delegate/MutationDelegateStatementReleaseTest.java @@ -0,0 +1,278 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.mapping.generated.delegate; + +import java.util.Date; +import java.util.Set; + +import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.Generated; +import org.hibernate.annotations.SourceType; +import org.hibernate.annotations.UpdateTimestamp; +import org.hibernate.engine.jdbc.JdbcLogging; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.generator.EventType; +import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.resource.jdbc.ResourceRegistry; +import org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl; + +import org.hibernate.testing.logger.LogInspectionHelper; +import org.hibernate.testing.logger.TriggerOnPrefixLogListener; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.jboss.logging.Logger; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Marco Belladelli + */ +@DomainModel( annotatedClasses = { + MutationDelegateStatementReleaseTest.IdentityOnly.class, + MutationDelegateStatementReleaseTest.IdentityAndValues.class, + MutationDelegateStatementReleaseTest.BaseEntity.class, + MutationDelegateStatementReleaseTest.ChildEntity.class, +} ) +@SessionFactory +@RequiresDialectFeature( feature = DialectFeatureChecks.SupportsIdentityColumns.class ) +@Jira( "https://hibernate.atlassian.net/browse/HHH-17688" ) +public class MutationDelegateStatementReleaseTest { + private TriggerOnPrefixLogListener trigger; + + @BeforeAll + public void setUp(SessionFactoryScope scope) { + trigger = new TriggerOnPrefixLogListener( Set.of( "Exception clearing", "Unable to release" ) ); + LogInspectionHelper.registerListener( + trigger, + Logger.getMessageLogger( + CoreMessageLogger.class, + ResourceRegistryStandardImpl.class.getName() + ) + ); + } + + @BeforeEach + public void reset() { + trigger.reset(); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + LogInspectionHelper.clearAllListeners( JdbcLogging.JDBC_MESSAGE_LOGGER ); + } + + @Test + public void testInsertGeneratedIdentityOnly(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final IdentityOnly entity = new IdentityOnly(); + session.persist( entity ); + session.flush(); + assertNoOrphanStatements( session ); + assertThat( entity.getId() ).isNotNull(); + assertThat( entity.getName() ).isNull(); + } ); + + assertThat( trigger.wasTriggered() ).as( "Exception encountered while releasing statement" ).isFalse(); + } + + @Test + public void testInsertGeneratedValuesAndIdentity(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final IdentityAndValues entity = new IdentityAndValues(); + session.persist( entity ); + session.flush(); + assertNoOrphanStatements( session ); + assertThat( entity.getId() ).isNotNull(); + assertThat( entity.getName() ).isEqualTo( "default_name" ); + } ); + + assertThat( trigger.wasTriggered() ).as( "Exception encountered while releasing statement" ).isFalse(); + } + + @Test + public void testUpdateGeneratedValuesAndIdentity(SessionFactoryScope scope) { + final Long id = scope.fromTransaction( session -> { + final IdentityAndValues entity = new IdentityAndValues(); + session.persist( entity ); + session.flush(); + return entity.getId(); + } ); + + scope.inTransaction( session -> { + final IdentityAndValues entity = session.find( IdentityAndValues.class, id ); + entity.setData( "changed" ); + session.flush(); + assertNoOrphanStatements( session ); + assertThat( entity.getUpdateDate() ).isNotNull(); + } ); + + assertThat( trigger.wasTriggered() ).as( "Exception encountered while releasing statement" ).isFalse(); + } + + @Test + public void testInsertChildEntity(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final ChildEntity entity = new ChildEntity(); + session.persist( entity ); + session.flush(); + assertNoOrphanStatements( session ); + assertThat( entity.getId() ).isNotNull(); + assertThat( entity.getName() ).isEqualTo( "default_name" ); + assertThat( entity.getChildName() ).isEqualTo( "default_child_name" ); + } ); + + assertThat( trigger.wasTriggered() ).as( "Exception encountered while releasing update statement" ).isFalse(); + } + + @Test + public void testUpdateChildEntity(SessionFactoryScope scope) { + final Long id = scope.fromTransaction( session -> { + final ChildEntity entity = new ChildEntity(); + session.persist( entity ); + session.flush(); + return entity.getId(); + } ); + + scope.inTransaction( session -> { + final ChildEntity entity = session.find( ChildEntity.class, id ); + entity.setData( "changed" ); + session.flush(); + assertNoOrphanStatements( session ); + assertThat( entity.getUpdateDate() ).isNotNull(); + assertThat( entity.getChildUpdateDate() ).isNotNull(); + } ); + + assertThat( trigger.wasTriggered() ).as( "Exception encountered while releasing update statement" ).isFalse(); + } + + private void assertNoOrphanStatements(SessionImplementor session) { + final ResourceRegistry resourceRegistry = session.getJdbcCoordinator() + .getLogicalConnection() + .getResourceRegistry(); + assertThat( resourceRegistry.hasRegisteredResources() ).as( "Expected no registered resources" ).isFalse(); + } + + @Entity( name = "IdentityOnly" ) + public static class IdentityOnly { + @Id + @GeneratedValue( strategy = GenerationType.IDENTITY ) + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + } + + @Entity( name = "IdentityAndValues" ) + @SuppressWarnings( "unused" ) + public static class IdentityAndValues { + @Id + @GeneratedValue( strategy = GenerationType.IDENTITY ) + private Long id; + + @Generated( event = EventType.INSERT ) + @ColumnDefault( "'default_name'" ) + private String name; + + @UpdateTimestamp( source = SourceType.DB ) + private Date updateDate; + + private String data; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Date getUpdateDate() { + return updateDate; + } + + public void setData(String data) { + this.data = data; + } + } + + @Entity( name = "BaseEntity" ) + @Inheritance( strategy = InheritanceType.JOINED ) + @SuppressWarnings( "unused" ) + public static class BaseEntity { + @Id + @GeneratedValue( strategy = GenerationType.IDENTITY ) + private Long id; + + @Generated( event = EventType.INSERT ) + @ColumnDefault( "'default_name'" ) + private String name; + + @UpdateTimestamp( source = SourceType.DB ) + private Date updateDate; + + @SuppressWarnings( "FieldCanBeLocal" ) + private String data; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Date getUpdateDate() { + return updateDate; + } + + public void setData(String data) { + this.data = data; + } + } + + @Entity( name = "ChildEntity" ) + @SuppressWarnings( "unused" ) + public static class ChildEntity extends BaseEntity { + @Generated( event = EventType.INSERT ) + @ColumnDefault( "'default_child_name'" ) + private String childName; + + @UpdateTimestamp( source = SourceType.DB ) + private Date childUpdateDate; + + public String getChildName() { + return childName; + } + + public Date getChildUpdateDate() { + return childUpdateDate; + } + } +} diff --git a/hibernate-core/src/test/resources/log4j2.properties b/hibernate-core/src/test/resources/log4j2.properties index 9acb5161974a..aed2d414612f 100644 --- a/hibernate-core/src/test/resources/log4j2.properties +++ b/hibernate-core/src/test/resources/log4j2.properties @@ -121,6 +121,9 @@ logger.scanning-coordinator.level=debug logger.abstract-persistent-collection.name=org.hibernate.collection.spi.AbstractPersistentCollection logger.abstract-persistent-collection.level=debug +logger.resource-registry-standard.name=org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl +logger.resource-registry-standard.level=debug + logger.cache.name=org.hibernate.cache logger.cache.level=trace logger.stat.name=org.hibernate.stat From 9f3676d690bd781a663d4a4f79fa3a891aee2e9a Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Mon, 29 Jan 2024 14:13:28 +0100 Subject: [PATCH 103/321] HHH-17688 Make statement release more consistent in mutation delegates --- .../internal/AbstractMutationExecutor.java | 3 ++- .../internal/MutationExecutorPostInsert.java | 5 +++- ...MutationExecutorPostInsertSingleTable.java | 3 ++- .../id/insert/AbstractReturningDelegate.java | 17 ++++++++++-- .../id/insert/AbstractSelectingDelegate.java | 15 ++++++++--- .../id/insert/GetGeneratedKeysDelegate.java | 26 +++++++++++-------- .../id/insert/InsertReturningDelegate.java | 3 --- .../SybaseJConnGetGeneratedKeysDelegate.java | 4 --- 8 files changed, 50 insertions(+), 26 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/AbstractMutationExecutor.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/AbstractMutationExecutor.java index ff89ab68e48a..8aee020e5ea3 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/AbstractMutationExecutor.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/AbstractMutationExecutor.java @@ -99,9 +99,10 @@ protected void performNonBatchedMutation( // If we get here the statement is needed - make sure it is resolved session.getJdbcServices().getSqlStatementLogger().logStatement( statementDetails.getSqlString() ); - valueBindings.beforeStatement( statementDetails ); try { + valueBindings.beforeStatement( statementDetails ); + final int affectedRowCount = session.getJdbcCoordinator() .getResultSetReturn() .executeUpdate( statementDetails.getStatement(), statementDetails.getSqlString() ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/MutationExecutorPostInsert.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/MutationExecutorPostInsert.java index ba765f90453d..e26df00997d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/MutationExecutorPostInsert.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/MutationExecutorPostInsert.java @@ -204,9 +204,10 @@ private void executeWithId( ); session.getJdbcServices().getSqlStatementLogger().logStatement( statementDetails.getSqlString() ); - valueBindings.beforeStatement( statementDetails ); try { + valueBindings.beforeStatement( statementDetails ); + final int affectedRowCount = session.getJdbcCoordinator() .getResultSetReturn() .executeUpdate( statementDetails.getStatement(), statementDetails.getSqlString() ); @@ -224,6 +225,8 @@ private void executeWithId( @Override public void release() { + // The mutation delegate already releases the identity insert statement + assert identityInsertStatementDetails.getStatement() == null; secondaryTablesStatementGroup.release(); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/MutationExecutorPostInsertSingleTable.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/MutationExecutorPostInsertSingleTable.java index 91133cabb1dc..3d4503a5077e 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/MutationExecutorPostInsertSingleTable.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/MutationExecutorPostInsertSingleTable.java @@ -112,7 +112,8 @@ public Object execute( @Override public void release() { - identityInsertStatementDetails.releaseStatement( session ); + // Nothing to do - the mutation delegate already releases the identity insert statement + assert identityInsertStatementDetails.getStatement() == null; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractReturningDelegate.java b/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractReturningDelegate.java index c822e08055b2..da6a7462eaca 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractReturningDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractReturningDelegate.java @@ -38,8 +38,21 @@ public Object performInsert( Object entity, SharedSessionContractImplementor session) { session.getJdbcServices().getSqlStatementLogger().logStatement( insertStatementDetails.getSqlString() ); - valueBindings.beforeStatement( insertStatementDetails ); - return executeAndExtract( insertStatementDetails.getSqlString(), insertStatementDetails.getStatement(), session ); + try { + valueBindings.beforeStatement( insertStatementDetails ); + return executeAndExtract( + insertStatementDetails.getSqlString(), + insertStatementDetails.getStatement(), + session + ); + } + finally { + if ( insertStatementDetails.getStatement() != null ) { + insertStatementDetails.releaseStatement( session ); + } + valueBindings.afterStatement( insertStatementDetails.getMutatingTableDetails() ); + session.getJdbcCoordinator().afterStatementExecution(); + } } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractSelectingDelegate.java b/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractSelectingDelegate.java index 8bcaece86b87..48f6d0b2652b 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractSelectingDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractSelectingDelegate.java @@ -70,10 +70,19 @@ public Object performInsert( final JdbcServices jdbcServices = session.getJdbcServices(); jdbcServices.getSqlStatementLogger().logStatement( insertStatementDetails.getSqlString() ); - jdbcValueBindings.beforeStatement( insertStatementDetails ); - jdbcCoordinator.getResultSetReturn() - .executeUpdate( insertStatementDetails.resolveStatement(), insertStatementDetails.getSqlString() ); + try { + jdbcValueBindings.beforeStatement( insertStatementDetails ); + jdbcCoordinator.getResultSetReturn() + .executeUpdate( insertStatementDetails.resolveStatement(), insertStatementDetails.getSqlString() ); + } + finally { + if ( insertStatementDetails.getStatement() != null ) { + insertStatementDetails.releaseStatement( session ); + } + jdbcValueBindings.afterStatement( insertStatementDetails.getMutatingTableDetails() ); + session.getJdbcCoordinator().afterStatementExecution(); + } // the insert is complete, select the generated id... diff --git a/hibernate-core/src/main/java/org/hibernate/id/insert/GetGeneratedKeysDelegate.java b/hibernate-core/src/main/java/org/hibernate/id/insert/GetGeneratedKeysDelegate.java index b711a1931f95..3c87f6541d1f 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/insert/GetGeneratedKeysDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/id/insert/GetGeneratedKeysDelegate.java @@ -100,10 +100,10 @@ public Object performInsert( jdbcServices.getSqlStatementLogger().logStatement( insertSql ); - final PreparedStatement insertStatement = insertStatementDetails.resolveStatement(); - jdbcValueBindings.beforeStatement( insertStatementDetails ); - try { + final PreparedStatement insertStatement = insertStatementDetails.resolveStatement(); + jdbcValueBindings.beforeStatement( insertStatementDetails ); + jdbcCoordinator.getResultSetReturn().executeUpdate( insertStatement, insertSql ); try { @@ -131,16 +131,20 @@ public Object performInsert( } } } - finally { - jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( insertStatement ); + catch (SQLException e) { + throw jdbcServices.getSqlExceptionHelper().convert( + e, + "Unable to extract generated-keys ResultSet", + insertSql + ); } } - catch (SQLException e) { - throw jdbcServices.getSqlExceptionHelper().convert( - e, - "Unable to extract generated-keys ResultSet", - insertSql - ); + finally { + if ( insertStatementDetails.getStatement() != null ) { + insertStatementDetails.releaseStatement( session ); + } + jdbcValueBindings.afterStatement( insertStatementDetails.getMutatingTableDetails() ); + jdbcCoordinator.afterStatementExecution(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/id/insert/InsertReturningDelegate.java b/hibernate-core/src/main/java/org/hibernate/id/insert/InsertReturningDelegate.java index f228ff7f5351..93ee920b430d 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/insert/InsertReturningDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/id/insert/InsertReturningDelegate.java @@ -77,9 +77,6 @@ protected Object executeAndExtract( insertSql ); } - finally { - jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( resultSet, insertStatement ); - } } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/id/insert/SybaseJConnGetGeneratedKeysDelegate.java b/hibernate-core/src/main/java/org/hibernate/id/insert/SybaseJConnGetGeneratedKeysDelegate.java index 904fd643e1d1..7d78a31af519 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/insert/SybaseJConnGetGeneratedKeysDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/id/insert/SybaseJConnGetGeneratedKeysDelegate.java @@ -73,9 +73,5 @@ public Object executeAndExtract( insertSql ); } - finally { - jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( resultSet, insertStatement ); - jdbcCoordinator.afterStatementExecution(); - } } } From 0403d12ea99be89bc1ae67303bf3578c525b06da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 6 Feb 2024 11:01:25 +0100 Subject: [PATCH 104/321] Move setting of net.bytebuddy.experimental to the Jenkinsfile The hope is that whenever we add a new JDK version to test, we'll notice this setting, will try to remove it and upgrade bytebuddy if necessary. This would avoid mess-ups like the one that caused https://github.com/hibernate/hibernate-orm/pull/7790 --- Jenkinsfile | 6 +++++- gradle/java-module.gradle | 15 --------------- hibernate-core/hibernate-core.gradle | 6 ------ .../hibernate-jpamodelgen.gradle | 6 ------ 4 files changed, 5 insertions(+), 28 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 4abfcc4a6821..bc433e6b071e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -49,7 +49,11 @@ stage('Configure') { new BuildEnvironment( testJdkVersion: '20', testJdkLauncherArgs: '--enable-preview' ), new BuildEnvironment( testJdkVersion: '21', testJdkLauncherArgs: '--enable-preview' ), new BuildEnvironment( testJdkVersion: '22', testJdkLauncherArgs: '--enable-preview' ), - new BuildEnvironment( testJdkVersion: '23', testJdkLauncherArgs: '--enable-preview' ) + // The following JDKs aren't supported by Hibernate ORM out-of-the box yet: + // they require the use of -Dnet.bytebuddy.experimental=true. + // Make sure to remove that argument as soon as possible + // -- generally that requires upgrading bytebuddy after the JDK goes GA. + new BuildEnvironment( testJdkVersion: '23', testJdkLauncherArgs: '--enable-preview -Dnet.bytebuddy.experimental=true' ) ]; if ( env.CHANGE_ID ) { diff --git a/gradle/java-module.gradle b/gradle/java-module.gradle index d7f9e5454899..a3c1beeea41c 100644 --- a/gradle/java-module.gradle +++ b/gradle/java-module.gradle @@ -294,21 +294,6 @@ test { jvmArgs '-XX:+StartAttachListener' } -// Enable the experimental features of ByteBuddy with JDK 22+ -test { - // We need to test the *launcher* version, - // because some tests will use Mockito (and thus Bytebuddy) to mock/spy - // classes that are part of the JDK, - // and those classes always have bytecode matching the version of the launcher. - // So for example, when using a JDK22 launcher and compiling tests with --release 21, - // Bytebuddy will still encounter classes with Java 22 bytecode. - if ( jdkVersions.test.launcher.asInt() >= 22 ) { - logger.warn( "The version of Java bytecode that will be tested is not supported by Bytebuddy by default. " + - " Setting 'net.bytebuddy.experimental=true'." ) - systemProperty 'net.bytebuddy.experimental', true - } -} - test { if ( project.findProperty( 'log-test-progress' )?.toString()?.toBoolean() ) { // Log a statement for each test. diff --git a/hibernate-core/hibernate-core.gradle b/hibernate-core/hibernate-core.gradle index 71865487d3e5..e00ccdbffa5b 100644 --- a/hibernate-core/hibernate-core.gradle +++ b/hibernate-core/hibernate-core.gradle @@ -298,12 +298,6 @@ if ( jdkVersions.test.release.asInt() >= 17 && jdkVersions.explicit ) { useJUnitPlatform() testClassesDirs = sourceSets.testJava17.output.classesDirs classpath = sourceSets.testJava17.runtimeClasspath - - if ( jdkVersions.test.launcher.asInt() >= 19 ) { - logger.warn( "The version of Java bytecode that will be tested is not supported by Byte Buddy by default. " + - " Setting 'net.bytebuddy.experimental=true'." ) - systemProperty 'net.bytebuddy.experimental', true - } } testClasses.dependsOn compileTestJava17Java diff --git a/tooling/metamodel-generator/hibernate-jpamodelgen.gradle b/tooling/metamodel-generator/hibernate-jpamodelgen.gradle index 66cc31439fe7..b41da6d2a69f 100644 --- a/tooling/metamodel-generator/hibernate-jpamodelgen.gradle +++ b/tooling/metamodel-generator/hibernate-jpamodelgen.gradle @@ -61,12 +61,6 @@ if ( jdkVersions.test.release.asInt() >= 17 && jdkVersions.explicit ) { javaLauncher = javaToolchains.launcherFor { languageVersion = jdkVersions.test.launcher } - - if ( jdkVersions.test.launcher.asInt() >= 19 ) { - logger.warn( "The version of Java bytecode that will be tested is not supported by Bytebuddy by default. " + - " Setting 'net.bytebuddy.experimental=true'." ) - systemProperty 'net.bytebuddy.experimental', true - } } } else { sourceSets { From e7f9875dbeab3ada2a63f7526294a7c22fdf2ade Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 6 Feb 2024 14:18:06 +0100 Subject: [PATCH 105/321] HHH-16974 Add test for issue --- .../QueryComparingAssociationToNullTest.java | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/hql/QueryComparingAssociationToNullTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/QueryComparingAssociationToNullTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/QueryComparingAssociationToNullTest.java new file mode 100644 index 000000000000..6e88b44a0c30 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/QueryComparingAssociationToNullTest.java @@ -0,0 +1,115 @@ +package org.hibernate.orm.test.hql; + +import java.time.LocalDate; + +import org.hibernate.dialect.DerbyDialect; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.SkipForDialect; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Query; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@DomainModel( + annotatedClasses = { + QueryComparingAssociationToNullTest.Parent.class, + QueryComparingAssociationToNullTest.Child.class, + } +) +@SessionFactory +@JiraKey("HHH-16974") +@SkipForDialect( dialectClass = DerbyDialect.class, reason = "it does not like `= null`") +public class QueryComparingAssociationToNullTest { + + @Test + public void testQuery(SessionFactoryScope scope) { + LocalDate date = LocalDate.now(); + scope.inTransaction( + session -> { + Child child = new Child( 1, "first" ); + Parent parent = new Parent( 1, date, child ); + session.persist( child ); + session.persist( parent ); + } + ); + + scope.inTransaction( + session -> { + Query query = session.createQuery( + "SELECT p FROM Parent p WHERE p.child = NULL ", + Parent.class + ); + query.getResultList(); + } + ); + + scope.inTransaction( + session -> { + Query query = session.createQuery( + "SELECT p FROM Parent p WHERE p.child = NULL OR p.date = :date ", + Parent.class + ).setParameter( "date", date ); + assertThat(query.getResultList().size()).isEqualTo( 1 ); + } + ); + + scope.inTransaction( + session -> { + Query query = session.createQuery( + "SELECT p FROM Parent p WHERE p.child = NULL OR p.date = NULL ", + Parent.class + ); + query.getResultList(); + } + ); + } + + @Entity(name = "Parent") + public static class Parent { + + @Id + private Integer id; + + @Column(name = "COLUMN_DATE") + private LocalDate date; + + @ManyToOne + private Child child; + + + public Parent() { + } + + public Parent(Integer id, LocalDate date, Child child) { + this.id = id; + this.date = date; + this.child = child; + } + } + + @Entity(name = "Child") + public static class Child { + @Id + private Integer id; + + private String name; + + public Child() { + } + + public Child(Integer id, String name) { + this.id = id; + this.name = name; + } + } + +} From e9946dbb1c35ea93033a0f9de244e6d1fb1b42df Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 6 Feb 2024 14:17:51 +0100 Subject: [PATCH 106/321] HHH-16974 IllegalStateException Unsupported tuple comparison combination --- .../org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index 8af30b45edc3..bc907988be13 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -7734,7 +7734,8 @@ else if ( needsTupleComparisonEmulation( operator ) ) { else if ( ( rhsTuple = SqlTupleContainer.getSqlTuple( comparisonPredicate.getRightHandExpression() ) ) != null ) { final Expression lhsExpression = comparisonPredicate.getLeftHandExpression(); - if ( lhsExpression instanceof SelectStatement && ( (SelectStatement) lhsExpression ).getQueryPart() instanceof QueryGroup ) { + if ( lhsExpression instanceof SqlTupleContainer + || lhsExpression instanceof SelectStatement && ( (SelectStatement) lhsExpression ).getQueryPart() instanceof QueryGroup ) { if ( rhsTuple.getExpressions().size() == 1 ) { // Special case for tuples with arity 1 as any DBMS supports scalar IN predicates renderComparison( From 658e9bc21583d7e3296d982fe70c75af7eeea9db Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Thu, 1 Feb 2024 10:16:56 +0100 Subject: [PATCH 107/321] HHH-17693 Add test for issue --- .../ConvertedAttributesTypecheckTest.java | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConvertedAttributesTypecheckTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConvertedAttributesTypecheckTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConvertedAttributesTypecheckTest.java new file mode 100644 index 000000000000..5b36def8cd5a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConvertedAttributesTypecheckTest.java @@ -0,0 +1,158 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.mapping.converted.converter; + +import java.time.Duration; +import java.util.Set; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Convert; +import jakarta.persistence.Converter; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Marco Belladelli + */ +@DomainModel( annotatedClasses = ConvertedAttributesTypecheckTest.TestEntity.class ) +@SessionFactory +@Jira( "https://hibernate.atlassian.net/browse/HHH-17693" ) +public class ConvertedAttributesTypecheckTest { + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> session.persist( new TestEntity( + Set.of( "one", "two" ), + "123", + String.valueOf( Duration.ofDays( 3 ).toMillis() ) + ) ) ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createMutationQuery( "delete from TestEntity" ).executeUpdate() ); + } + + @Test + public void testLikeOnConvertedString(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final TestEntity result = session.createQuery( + "from TestEntity where convertedString like 'one%'", + TestEntity.class + ).getSingleResult(); + assertThat( result.getConvertedString() ).contains( "one" ); + } ); + } + + @Test + public void testUnaryExpressionOnConvertedNumber(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final String result = session.createQuery( + "select -convertedNumber from TestEntity", + String.class + ).getSingleResult(); + assertThat( result ).isEqualTo( "-123" ); + } ); + } + + @Test + public void testFromDurationExpressionOnConvertedDuration(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final String result = session.createQuery( + "select convertedDuration by day from TestEntity", + String.class + ).getSingleResult(); + assertThat( Long.parseLong( result ) ).isEqualTo( Duration.ofDays( 3 ).toMillis() ); + } ); + } + + @Entity( name = "TestEntity" ) + public static class TestEntity { + @Id + @GeneratedValue + public Long id; + + @Convert( converter = SetConverter.class ) + public Set convertedString; + + @Convert( converter = StringConverter.class ) + public String convertedNumber; + + @Convert( converter = DurationConverter.class ) + public String convertedDuration; + + public TestEntity() { + } + + public TestEntity(Set convertedString, String convertedNumber, String convertedDuration) { + this.convertedString = convertedString; + this.convertedNumber = convertedNumber; + this.convertedDuration = convertedDuration; + } + + public Set getConvertedString() { + return convertedString; + } + + public String getConvertedNumber() { + return convertedNumber; + } + + public String getConvertedDuration() { + return convertedDuration; + } + } + + @Converter + public static class SetConverter implements AttributeConverter, String> { + @Override + public String convertToDatabaseColumn(final Set attribute) { + return attribute == null ? null : String.join( ",", attribute ); + } + + @Override + public Set convertToEntityAttribute(final String dbData) { + return dbData == null ? null : Set.of( dbData.split( "," ) ); + } + } + + @Converter + public static class StringConverter implements AttributeConverter { + @Override + public Integer convertToDatabaseColumn(final String attribute) { + return attribute == null ? null : Integer.valueOf( attribute ); + } + + @Override + public String convertToEntityAttribute(final Integer dbData) { + return dbData == null ? null : dbData.toString(); + } + } + + @Converter + public static class DurationConverter implements AttributeConverter { + @Override + public Duration convertToDatabaseColumn(final String attribute) { + return attribute == null ? null : Duration.ofMillis( Long.parseLong( attribute ) ); + } + + @Override + public String convertToEntityAttribute(final Duration dbData) { + return dbData == null ? null : String.valueOf( dbData.toMillis() ); + } + } +} From 6138c76c7277a396eb4fe9558dda52925df36615 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Thu, 1 Feb 2024 10:17:16 +0100 Subject: [PATCH 108/321] HHH-17693 Fix typecheck assertions for converted properties Also introduce a custom `DurationJdbcType`, mainly for validation purposes. --- .../process/spi/MetadataBuildingProcess.java | 2 +- .../AbstractPostgreSQLStructJdbcType.java | 1 + .../org/hibernate/dialect/JsonHelper.java | 1 + .../java/org/hibernate/dialect/XmlHelper.java | 1 + .../util/config/ConfigurationHelper.java | 2 +- .../query/sqm/internal/TypecheckUtil.java | 33 ++++----------- .../java/org/hibernate/type/SqlTypes.java | 15 +++++++ .../descriptor/jdbc/DurationJdbcType.java | 42 +++++++++++++++++++ .../type/descriptor/jdbc/JdbcType.java | 6 +++ .../jdbc/internal/JdbcTypeBaseline.java | 2 + 10 files changed, 79 insertions(+), 26 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/DurationJdbcType.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java index a5353f12c08b..d18696b988a0 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java @@ -686,7 +686,7 @@ public void contributeType(CompositeUserType type) { ); } else { - addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INTERVAL_SECOND, SqlTypes.NUMERIC ); + addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INTERVAL_SECOND, SqlTypes.DURATION ); } addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INET, SqlTypes.VARBINARY ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractPostgreSQLStructJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractPostgreSQLStructJdbcType.java index 49bd79e360a1..ea433d40a125 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractPostgreSQLStructJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractPostgreSQLStructJdbcType.java @@ -776,6 +776,7 @@ private void serializeBasicTo( case SqlTypes.DOUBLE: case SqlTypes.DECIMAL: case SqlTypes.NUMERIC: + case SqlTypes.DURATION: jdbcJavaType.appendEncodedString( appender, jdbcJavaType.unwrap( diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/dialect/JsonHelper.java index 91b5b564d45e..fa6712b55f0f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/JsonHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/JsonHelper.java @@ -194,6 +194,7 @@ else if ( mappedType instanceof BasicType ) { break; case SqlTypes.DECIMAL: case SqlTypes.NUMERIC: + case SqlTypes.DURATION: case SqlTypes.UUID: // These types need to be serialized as JSON string, but don't have a need for escaping appender.append( '"' ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/XmlHelper.java b/hibernate-core/src/main/java/org/hibernate/dialect/XmlHelper.java index 69e8e9fec23a..cd68779d7e14 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/XmlHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/XmlHelper.java @@ -564,6 +564,7 @@ private static void serializeValueTo(XMLAppender appender, SelectableMapping sel case SqlTypes.DOUBLE: case SqlTypes.DECIMAL: case SqlTypes.NUMERIC: + case SqlTypes.DURATION: jdbcJavaType.appendEncodedString( appender, jdbcJavaType.unwrap( diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/config/ConfigurationHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/config/ConfigurationHelper.java index 979a52271c7a..cd287a3cd4f2 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/config/ConfigurationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/config/ConfigurationHelper.java @@ -546,7 +546,7 @@ public static synchronized int getPreferredSqlTypeCodeForDuration(StandardServic return explicitSetting; } - return SqlTypes.NUMERIC; + return SqlTypes.DURATION; } @Incubating diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java index dd84d30823cb..359701042bd1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java @@ -507,39 +507,24 @@ private static boolean isNumberArray(SqmExpressible expressible) { public static void assertString(SqmExpression expression) { final SqmExpressible nodeType = expression.getNodeType(); if ( nodeType != null ) { - final Class javaType = nodeType.getExpressibleJavaType().getJavaTypeClass(); - if ( javaType != String.class && javaType != char[].class ) { + final DomainType domainType = nodeType.getSqmType(); + if ( !( domainType instanceof JdbcMapping ) || !( (JdbcMapping) domainType ).getJdbcType().isStringLike() ) { throw new SemanticException( "Operand of 'like' is of type '" + nodeType.getTypeName() + - "' which is not a string (it is not an instance of 'java.lang.String' or 'char[]')" + "' which is not a string (its JDBC type code is not string-like)" ); } } } -// public static void assertNumeric(SqmExpression expression, BinaryArithmeticOperator op) { -// final SqmExpressible nodeType = expression.getNodeType(); -// if ( nodeType != null ) { -// final Class javaType = nodeType.getExpressibleJavaType().getJavaTypeClass(); -// if ( !Number.class.isAssignableFrom( javaType ) -// && !Temporal.class.isAssignableFrom( javaType ) -// && !TemporalAmount.class.isAssignableFrom( javaType ) -// && !java.util.Date.class.isAssignableFrom( javaType ) ) { -// throw new SemanticException( "Operand of " + op.getOperatorSqlText() -// + " is of type '" + nodeType.getTypeName() + "' which is not a numeric type" -// + " (it is not an instance of 'java.lang.Number', 'java.time.Temporal', or 'java.time.TemporalAmount')" ); -// } -// } -// } - public static void assertDuration(SqmExpression expression) { final SqmExpressible nodeType = expression.getNodeType(); if ( nodeType != null ) { - final Class javaType = nodeType.getExpressibleJavaType().getJavaTypeClass(); - if ( !TemporalAmount.class.isAssignableFrom( javaType ) ) { + final DomainType domainType = nodeType.getSqmType(); + if ( !( domainType instanceof JdbcMapping ) || !( (JdbcMapping) domainType ).getJdbcType().isDuration() ) { throw new SemanticException( "Operand of 'by' is of type '" + nodeType.getTypeName() + - "' which is not a duration (it is not an instance of 'java.time.TemporalAmount')" + "' which is not a duration (its JDBC type code is not duration-like)" ); } } @@ -548,11 +533,11 @@ public static void assertDuration(SqmExpression expression) { public static void assertNumeric(SqmExpression expression, UnaryArithmeticOperator op) { final SqmExpressible nodeType = expression.getNodeType(); if ( nodeType != null ) { - final Class javaType = nodeType.getExpressibleJavaType().getJavaTypeClass(); - if ( !Number.class.isAssignableFrom( javaType ) ) { + final DomainType domainType = nodeType.getSqmType(); + if ( !( domainType instanceof JdbcMapping ) || !( (JdbcMapping) domainType ).getJdbcType().isNumber() ) { throw new SemanticException( "Operand of " + op.getOperatorChar() + " is of type '" + nodeType.getTypeName() + - "' which is not a numeric type (it is not an instance of 'java.lang.Number')" + "' which is not a numeric type (its JDBC type code is not numeric)" ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/SqlTypes.java b/hibernate-core/src/main/java/org/hibernate/type/SqlTypes.java index 6eaff7dc81fc..6ca2212e5529 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/SqlTypes.java +++ b/hibernate-core/src/main/java/org/hibernate/type/SqlTypes.java @@ -498,6 +498,14 @@ public class SqlTypes { */ public static final int TIME_UTC = 3007; + /** + * A type code representing a "virtual mapping" of {@linkplain java.time.Duration}. + * + * @see Types#NUMERIC + * @see org.hibernate.type.descriptor.jdbc.DurationJdbcType + */ + public static final int DURATION = 3015; + // Interval types /** @@ -779,6 +787,13 @@ public static boolean isIntervalType(int typeCode) { return typeCode == INTERVAL_SECOND; } + /** + * Does the given typecode represent a {@code duration} type? + */ + public static boolean isDurationType(int typeCode) { + return typeCode == DURATION; + } + /** * Does the given typecode represent a SQL date or timestamp type? * @param typeCode a JDBC type code from {@link Types} diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/DurationJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/DurationJdbcType.java new file mode 100644 index 000000000000..9342b9b5284e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/DurationJdbcType.java @@ -0,0 +1,42 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.type.descriptor.jdbc; + +import java.sql.Types; +import java.time.Duration; + +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.WrapperOptions; + +/** + * Descriptor for {@link java.time.Duration}. + * + * @author Marco Belladelli + */ +public class DurationJdbcType extends NumericJdbcType { + public static final DurationJdbcType INSTANCE = new DurationJdbcType(); + + @Override + public int getDdlTypeCode() { + return Types.NUMERIC; + } + + @Override + public int getDefaultSqlTypeCode() { + return SqlTypes.DURATION; + } + + @Override + public String getFriendlyName() { + return "DURATION"; + } + + @Override + public String toString() { + return "DurationJdbcType"; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcType.java index dd03a423ac82..e2b26b3aba91 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcType.java @@ -287,6 +287,12 @@ default boolean isInterval() { return isIntervalType( getDdlTypeCode() ); } + default boolean isDuration() { + final int ddlTypeCode = getDefaultSqlTypeCode(); + return isDurationType( ddlTypeCode ) + || isIntervalType( ddlTypeCode ); + } + default CastType getCastType() { return getCastType( getDdlTypeCode() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcTypeBaseline.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcTypeBaseline.java index 2a276c66e753..b606122dd174 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcTypeBaseline.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcTypeBaseline.java @@ -18,6 +18,7 @@ import org.hibernate.type.descriptor.jdbc.DateJdbcType; import org.hibernate.type.descriptor.jdbc.DecimalJdbcType; import org.hibernate.type.descriptor.jdbc.DoubleJdbcType; +import org.hibernate.type.descriptor.jdbc.DurationJdbcType; import org.hibernate.type.descriptor.jdbc.FloatJdbcType; import org.hibernate.type.descriptor.jdbc.IntegerJdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType; @@ -66,6 +67,7 @@ public static void prime(BaselineTarget target) { target.addDescriptor( TimestampWithTimeZoneJdbcType.INSTANCE ); target.addDescriptor( TimeJdbcType.INSTANCE ); target.addDescriptor( TimeWithTimeZoneJdbcType.INSTANCE ); + target.addDescriptor( DurationJdbcType.INSTANCE ); target.addDescriptor( BinaryJdbcType.INSTANCE ); target.addDescriptor( VarbinaryJdbcType.INSTANCE ); From a2d3315067feb9a3af039b64c44358e84e6672d3 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 7 Feb 2024 12:01:28 +0100 Subject: [PATCH 109/321] HHH-16454 Add test for issue --- .../orm/test/filter/FilterWithILikeTest.java | 82 +++++++++++++++++++ .../orm/junit/DialectFeatureChecks.java | 7 ++ 2 files changed, 89 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/filter/FilterWithILikeTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/filter/FilterWithILikeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/filter/FilterWithILikeTest.java new file mode 100644 index 000000000000..e8718b4281f7 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/filter/FilterWithILikeTest.java @@ -0,0 +1,82 @@ +package org.hibernate.orm.test.filter; + +import org.hibernate.annotations.Filter; +import org.hibernate.annotations.FilterDef; +import org.hibernate.annotations.ParamDef; + +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +@DomainModel( + annotatedClasses = { + FilterWithILikeTest.TestEntity.class + } +) +@SessionFactory +@JiraKey("HHH-16464") +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsCaseInsensitiveLike.class) +public class FilterWithILikeTest { + + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + TestEntity filtered = new TestEntity( 1, "filtered" ); + session.persist( filtered ); + + TestEntity notFiltered = new TestEntity( 2, "not_filtered" ); + session.persist( notFiltered ); + } + ); + } + + @Test + public void testQuery(SessionFactoryScope scope) { + + scope.inTransaction( + session -> { + session.enableFilter( "nameFilter" ).setParameter( "name", "not_filtered" ); + session.createQuery( "from TestEntity " ).list(); + } + ); + } + + @FilterDef(name = "nameFilter", parameters = { + @ParamDef(name = "name", type = String.class) + }) + @Filter(name = "nameFilter", condition = "NAME ilike :name") + @Entity(name = "TestEntity") + public static class TestEntity { + @Id + private Integer id; + + @Column(name = "NAME") + private String name; + + public TestEntity() { + } + + public TestEntity(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java index f97b1d0cda14..55588f09189b 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java @@ -678,4 +678,11 @@ public boolean apply(Dialect dialect) { return dialect.getPreferredSqlTypeCodeForArray() != SqlTypes.VARBINARY; } } + + public static class SupportsCaseInsensitiveLike implements DialectFeatureCheck { + @Override + public boolean apply(Dialect dialect) { + return dialect.supportsCaseInsensitiveLike(); + } + } } From 6057d99b79aab9750b1aaac09154af8480d08ae7 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 7 Feb 2024 12:51:51 +0100 Subject: [PATCH 110/321] HHH-16454 PostgreSQL ILIKE Keyword is considered a column name when deducing alias injection points --- .../community/dialect/H2LegacyDialect.java | 15 +++++++++++++++ .../java/org/hibernate/dialect/H2Dialect.java | 11 +++++++++++ .../org/hibernate/dialect/PostgreSQLDialect.java | 1 + 3 files changed, 27 insertions(+) diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java index 73bd5da49a1b..76789d08de0c 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java @@ -940,4 +940,19 @@ public String rowId(String rowId) { public int rowIdSqlType() { return BIGINT; } + + @Override + public String getCaseInsensitiveLike() { + if ( getVersion().isSameOrAfter( 1, 4, 194 ) ) { + return "ilike"; + } + else { + return super.getCaseInsensitiveLike(); + } + } + + @Override + public boolean supportsCaseInsensitiveLike() { + return getVersion().isSameOrAfter( 1, 4, 194 ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java index 992283b6790a..e21b524406ce 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -935,4 +935,15 @@ public String createMarker(int position, JdbcType jdbcType) { return "?" + position; } } + + @Override + public String getCaseInsensitiveLike() { + return "ilike"; + } + + @Override + public boolean supportsCaseInsensitiveLike(){ + return true; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index 2804ca1ecd3d..ccd97ce4c969 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -153,6 +153,7 @@ public PostgreSQLDialect() { public PostgreSQLDialect(DialectResolutionInfo info) { this( info, PostgreSQLDriverKind.determineKind( info ) ); + registerKeywords( info ); } public PostgreSQLDialect(DatabaseVersion version) { From d35cd469958dd862c92c4b735780af819e6e3a10 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Fri, 2 Feb 2024 18:30:36 +0100 Subject: [PATCH 111/321] HHH-17704 Add test for issue --- .../DetachedProxyAsQueryParameterTest.java | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/proxy/DetachedProxyAsQueryParameterTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/DetachedProxyAsQueryParameterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/DetachedProxyAsQueryParameterTest.java new file mode 100644 index 000000000000..290e645ede07 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/DetachedProxyAsQueryParameterTest.java @@ -0,0 +1,172 @@ +package org.hibernate.orm.test.proxy; + +import org.hibernate.Hibernate; +import org.hibernate.LazyInitializationException; +import org.hibernate.query.Query; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.ManyToOne; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@DomainModel( + annotatedClasses = { + DetachedProxyAsQueryParameterTest.Department.class, + DetachedProxyAsQueryParameterTest.BasicDepartment.class, + DetachedProxyAsQueryParameterTest.SpecialDepartment.class, + DetachedProxyAsQueryParameterTest.Employee.class + } +) +@SessionFactory +@JiraKey("HHH-17704") +public class DetachedProxyAsQueryParameterTest { + + private static final Integer EMPLOYEE_ID = 1; + + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + SpecialDepartment department = new SpecialDepartment( 2, "dep1" ); + Employee employee = new Employee( EMPLOYEE_ID, "Fab", department ); + session.persist( department ); + session.persist( employee ); + } + ); + } + + @Test + public void testQuery(SessionFactoryScope scope) { + Employee employee = scope.fromTransaction( + session -> + session.find( Employee.class, EMPLOYEE_ID ) + ); + assertFalse( Hibernate.isInitialized( employee.getDepartment() ) ); + scope.inSession( + session -> { + Query query = session.createQuery( + "select e from Employee e where e.department = :department", + Employee.class + ); + query.setParameter( "department", employee.getDepartment() ); + query.list(); + } + ); + scope.inSession( + session -> { + session.createQuery( + "select d from Department d where d = :department" ) + .setParameter( "department", employee.getDepartment() ).list(); + } + ); + + + assertThrows( + LazyInitializationException.class, () -> { + scope.inSession( + session -> { + // In order to validate the parameter and check that it is an instance of SpecialDepartment + // the Department proxy have to be initialized but being a detached instance + // a LazyInitializationException is thrown + session.createQuery( + "select d from SpecialDepartment d where d = :department" + ).setParameter( "department", employee.getDepartment() ).list(); + } + ); + } + ); + } + + @Entity(name = "Department") + @Inheritance(strategy = InheritanceType.SINGLE_TABLE) + public static class Department { + @Id + private Integer id; + + public Department() { + } + + public Department(Integer id) { + this.id = id; + } + + public Integer getId() { + return id; + } + } + + @Entity(name = "BasicDepartment") + public static class BasicDepartment extends Department { + + + private String name; + + public BasicDepartment() { + } + + public BasicDepartment(Integer id, String name) { + super( id ); + this.name = name; + } + + + public String getName() { + return name; + } + } + + @Entity(name = "SpecialDepartment") + public static class SpecialDepartment extends BasicDepartment { + public SpecialDepartment() { + } + + public SpecialDepartment(Integer id, String name) { + super( id, name ); + } + } + + @Entity(name = "Employee") + public static class Employee { + + @Id + private Integer id; + + private String name; + + @ManyToOne(fetch = FetchType.LAZY) + private Department department; + + public Employee() { + } + + public Employee(Integer id, String name, Department department) { + this.id = id; + this.name = name; + this.department = department; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public Department getDepartment() { + return department; + } + } +} From e98503a80721de7513c6b5adffebdbc2d5bfee8e Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Fri, 2 Feb 2024 18:32:11 +0100 Subject: [PATCH 112/321] HHH-17704 Query using detached Proxy as parameter fails with LazyInitializationException --- .../descriptor/java/spi/EntityJavaType.java | 14 +++- .../test/proxy/ProxyAsQueryParameterTest.java | 84 ++++++++++++++----- 2 files changed, 74 insertions(+), 24 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java index a5c7e6760508..50445dae386a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java @@ -6,7 +6,7 @@ */ package org.hibernate.type.descriptor.java.spi; -import org.hibernate.Hibernate; +import org.hibernate.proxy.LazyInitializer; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.AbstractClassJavaType; import org.hibernate.type.descriptor.java.IncomparableComparator; @@ -14,6 +14,8 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; +import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; + /** * Uses object identity for {@code equals}/{@code hashCode} as we ensure that internally. * @@ -44,7 +46,15 @@ public boolean areEqual(T one, T another) { @Override public boolean isInstance(Object value) { - return getJavaTypeClass().isAssignableFrom( Hibernate.getClassLazy( value ) ); + final LazyInitializer lazyInitializer = extractLazyInitializer( value ); + final Class javaTypeClass = getJavaTypeClass(); + if ( lazyInitializer != null ) { + return javaTypeClass.isAssignableFrom( lazyInitializer.getPersistentClass() ) + || javaTypeClass.isAssignableFrom( lazyInitializer.getImplementationClass() ); + } + else { + return javaTypeClass.isAssignableFrom( value.getClass() ); + } } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyAsQueryParameterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyAsQueryParameterTest.java index b870897bccb1..51776c7ccf92 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyAsQueryParameterTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyAsQueryParameterTest.java @@ -6,6 +6,8 @@ */ package org.hibernate.orm.test.proxy; +import java.util.List; + import org.hibernate.Hibernate; import org.hibernate.testing.orm.junit.DomainModel; @@ -23,15 +25,20 @@ import static org.assertj.core.api.Assertions.assertThat; -@DomainModel( annotatedClasses = { +@DomainModel(annotatedClasses = { ProxyAsQueryParameterTest.Product.class, ProxyAsQueryParameterTest.Vendor.class, ProxyAsQueryParameterTest.CarVendor.class, + ProxyAsQueryParameterTest.LuxuryCarVendor.class, ProxyAsQueryParameterTest.Producer.class, -} ) +}) @SessionFactory -@Jira( "https://hibernate.atlassian.net/browse/HHH-17467" ) +@Jira("https://hibernate.atlassian.net/browse/HHH-17467") public class ProxyAsQueryParameterTest { + + private static final Integer PRODUCT_ID = 1; + private static final Integer LUXURY_PRODUCT_ID = 2; + @BeforeAll public void setUp(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -39,8 +46,13 @@ public void setUp(SessionFactoryScope scope) { session.persist( vendor ); final Producer producer = new Producer( 1, "producer_1" ); session.persist( producer ); - final Product product = new Product( 1, vendor, producer ); + final Product product = new Product( PRODUCT_ID, vendor, producer ); session.persist( product ); + + final LuxuryCarVendor luxuryCarVendor = new LuxuryCarVendor( 2, "vendor_2", "luxury" ); + session.persist( luxuryCarVendor ); + final Product luxuryProduct = new Product( LUXURY_PRODUCT_ID, luxuryCarVendor, producer ); + session.persist( luxuryProduct ); } ); } @@ -55,29 +67,31 @@ public void tearDown(SessionFactoryScope scope) { @Test public void testProxyParam(SessionFactoryScope scope) { scope.inTransaction( session -> { - final Product product = session.createQuery( "from Product p", Product.class ).getSingleResult(); + final Product product = session.createQuery( "from Product p where p.id = :productId", Product.class ) + .setParameter( "productId", PRODUCT_ID ) + .getSingleResult(); assertThat( Hibernate.isInitialized( product.getProducer() ) ).isFalse(); - final Product result = session.createQuery( + final List results = session.createQuery( "from Product p where p.producer = :producer", Product.class - ).setParameter( "producer", product.getProducer() ).getSingleResult(); - // The proxy should not have been initialized since Producer doesn't have subclasses - assertThat( Hibernate.isInitialized( product.getProducer() ) ).isFalse(); - assertThat( result.getProducer().getId() ).isEqualTo( product.getProducer().getId() ); + ).setParameter( "producer", product.getProducer() ).getResultList(); + assertThat( results.size() ).isEqualTo( 2 ); + assertThat( results.get( 0 ).getProducer().getId() ).isEqualTo( product.getProducer().getId() ); + assertThat( results.get( 1 ).getProducer().getId() ).isEqualTo( product.getProducer().getId() ); } ); } @Test public void testProxyParamWithSubclasses(SessionFactoryScope scope) { scope.inTransaction( session -> { - final Product product = session.createQuery( "from Product p", Product.class ).getSingleResult(); + final Product product = session.createQuery( "from Product p where p.id = :productId", Product.class ) + .setParameter( "productId", PRODUCT_ID ) + .getSingleResult(); assertThat( Hibernate.isInitialized( product.getVendor() ) ).isFalse(); final Product result = session.createQuery( "from Product p where p.vendor = :vendor", Product.class ).setParameter( "vendor", product.getVendor() ).getSingleResult(); - // The proxy will have been initialized since Vendor has subclasses - assertThat( Hibernate.isInitialized( product.getVendor() ) ).isTrue(); assertThat( result.getVendor().getId() ).isEqualTo( product.getVendor().getId() ); } ); } @@ -85,19 +99,34 @@ public void testProxyParamWithSubclasses(SessionFactoryScope scope) { @Test public void testSubclassProxyParam(SessionFactoryScope scope) { scope.inTransaction( session -> { - final Product product = session.createQuery( "from Product p", Product.class ).getSingleResult(); + final Product product = session.createQuery( "from Product p where p.id = :productId", Product.class ) + .setParameter( "productId", PRODUCT_ID ) + .getSingleResult(); assertThat( Hibernate.isInitialized( product.getVendor() ) ).isFalse(); final CarVendor result = session.createQuery( "from CarVendor v where v = :vendor", CarVendor.class ).setParameter( "vendor", product.getVendor() ).getSingleResult(); - // The proxy should have been initialized since Vendor has subclasses - assertThat( Hibernate.isInitialized( product.getVendor() ) ).isTrue(); assertThat( result.getId() ).isEqualTo( product.getVendor().getId() ); } ); } - @Entity( name = "Producer" ) + @Test + public void testSubSubclassProxyParam(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final Product product = session.createQuery( "from Product p where p.id = :productId", Product.class ) + .setParameter( "productId", LUXURY_PRODUCT_ID ) + .getSingleResult(); + assertThat( Hibernate.isInitialized( product.getVendor() ) ).isFalse(); + final LuxuryCarVendor result = session.createQuery( + "from CarVendor v where v = :vendor", + LuxuryCarVendor.class + ).setParameter( "vendor", product.getVendor() ).getSingleResult(); + assertThat( result.getId() ).isEqualTo( product.getVendor().getId() ); + } ); + } + + @Entity(name = "Producer") public static class Producer { @Id private Integer id; @@ -120,7 +149,7 @@ public String getName() { } } - @Entity( name = "Vendor" ) + @Entity(name = "Vendor") public static class Vendor { @Id private Integer id; @@ -143,7 +172,7 @@ public String getName() { } } - @Entity( name = "CarVendor" ) + @Entity(name = "CarVendor") public static class CarVendor extends Vendor { private String dealership; @@ -160,7 +189,18 @@ public String getDealership() { } } - @Entity( name = "Product" ) + @Entity(name = "LuxuryCarVendor") + public static class LuxuryCarVendor extends CarVendor { + + public LuxuryCarVendor() { + } + + public LuxuryCarVendor(int id, String name, String dealership) { + super( id, name, dealership ); + } + } + + @Entity(name = "Product") public static final class Product { private Integer id; private Vendor vendor; @@ -184,7 +224,7 @@ public void setId(Integer id) { this.id = id; } - @ManyToOne( fetch = FetchType.LAZY ) + @ManyToOne(fetch = FetchType.LAZY) public Vendor getVendor() { return vendor; } @@ -193,7 +233,7 @@ public void setVendor(Vendor vendor) { this.vendor = vendor; } - @ManyToOne( fetch = FetchType.LAZY ) + @ManyToOne(fetch = FetchType.LAZY) public Producer getProducer() { return producer; } From 3e49c0d71c07d23392b13b368cbc422901ec2640 Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Thu, 8 Feb 2024 13:17:38 +0000 Subject: [PATCH 113/321] Pre-steps for release : `6.4.4.Final` --- changelog.txt | 19 +++++++++++++++++++ gradle/version.properties | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 54cbcf3a7197..679c7e243647 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,25 @@ Hibernate 6 Changelog Note: Please refer to JIRA to learn more about each issue. +Changes in 6.4.4.Final (February 08, 2024) +------------------------------------------------------------------------------------------------------------------------ + +https://hibernate.atlassian.net/projects/HHH/versions/32259 + +** Bug + * [HHH-17705] - NullPointerException during enhancement when using the default BytecodeProvider in Wildfly + * [HHH-17704] - Query using detached Proxy as parameter fails with LazyInitializationException + * [HHH-17693] - Cannot use 'like' operand when an AttributeConverter is used to convert the attribute in a String + * [HHH-17688] - Mutation statements using delegates do not consistently release statements + * [HHH-17662] - Memory leak in type registry + * [HHH-16974] - IllegalStateException Unsupported tuple comparison combination + * [HHH-16454] - PostgreSQL ILIKE Keyword is considered a column name when deducing alias injection points + +** Task + * [HHH-17713] - Upgrade ByteBuddy to 1.14.11 + * [HHH-17708] - Fix formatting/anchor in documentation of @Struct + + Changes in 6.4.3.Final (February 02, 2024) ------------------------------------------------------------------------------------------------------------------------ diff --git a/gradle/version.properties b/gradle/version.properties index f77490443d1c..f41fab576595 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -hibernateVersion=6.4.4-SNAPSHOT \ No newline at end of file +hibernateVersion=6.4.4.Final \ No newline at end of file From faad4059046a50ea8db0b8ded99ebc924c268add Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Thu, 8 Feb 2024 13:21:32 +0000 Subject: [PATCH 114/321] Post-steps for release : `6.4.4.Final` --- gradle/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/version.properties b/gradle/version.properties index f41fab576595..42020426a910 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -hibernateVersion=6.4.4.Final \ No newline at end of file +hibernateVersion=6.4.5-SNAPSHOT \ No newline at end of file From 7463ce89e3b9a42455eb07e4a9af4a0ab1f86ad0 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 12 Jan 2024 17:49:00 +0100 Subject: [PATCH 115/321] HHH-17637 improve an error message --- .../function/ArgumentTypesValidator.java | 71 ++++++++++++------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/ArgumentTypesValidator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/ArgumentTypesValidator.java index 722a7a230759..1e219bff12a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/ArgumentTypesValidator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/ArgumentTypesValidator.java @@ -27,6 +27,7 @@ import org.hibernate.type.JavaObjectType; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.spi.JdbcTypeRecommendationException; +import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; import org.hibernate.type.spi.TypeConfiguration; @@ -103,7 +104,7 @@ public void validate( switch (type) { case TEMPORAL_UNIT: if ( !(argument instanceof SqmExtractUnit) && !(argument instanceof SqmDurationUnit) ) { - throwError(type, Object.class, functionName, count); + throwError(type, Object.class, null, functionName, count); } break; // the following are not really necessary for the functions we have today @@ -112,12 +113,12 @@ public void validate( // something crazy by the parser case TRIM_SPEC: if ( !(argument instanceof SqmTrimSpecification) ) { - throwError(type, Object.class, functionName, count); + throwError(type, Object.class, null, functionName, count); } break; case COLLATION: if ( !(argument instanceof SqmCollation) ) { - throwError(type, Object.class, functionName, count); + throwError(type, Object.class, null, functionName, count); } break; case NO_UNTYPED: @@ -149,9 +150,11 @@ private void checkArgumentType( if ( !isUnknown( javaType ) ) { DomainType domainType = argument.getExpressible().getSqmType(); if ( domainType instanceof JdbcMapping ) { + JdbcType jdbcType = ((JdbcMapping) domainType).getJdbcType(); checkArgumentType( count, functionName, type, - ((JdbcMapping) domainType).getJdbcType().getDefaultSqlTypeCode(), + jdbcType.getDefaultSqlTypeCode(), + jdbcType.getFriendlyName(), javaType.getJavaTypeClass() ); } @@ -161,6 +164,7 @@ private void checkArgumentType( checkArgumentType( count, functionName, type, getJdbcType( indicators, javaType ), + null, javaType.getJavaTypeClass() ); } @@ -229,6 +233,7 @@ private int validateArgument(int paramNumber, JdbcMappingContainer expressionTyp functionName, type, mapping.getJdbcType().getDefaultSqlTypeCode(), + mapping.getJdbcType().getFriendlyName(), mapping.getJavaTypeDescriptor().getJavaType() ); } @@ -236,7 +241,8 @@ private int validateArgument(int paramNumber, JdbcMappingContainer expressionTyp return paramNumber; } - private static void checkArgumentType(int paramNumber, String functionName, FunctionParameterType type, int code, Type javaType) { + private static void checkArgumentType( + int paramNumber, String functionName, FunctionParameterType type, int code, String sqlType, Type javaType) { switch (type) { case COMPARABLE: if ( !isCharacterType(code) && !isTemporalType(code) && !isNumericType(code) && !isEnumType( code ) @@ -246,68 +252,83 @@ private static void checkArgumentType(int paramNumber, String functionName, Func // as a special case, we consider a binary column // comparable when it is mapped by a Java UUID && !( javaType == java.util.UUID.class && code == Types.BINARY ) ) { - throwError(type, javaType, functionName, paramNumber); + throwError(type, javaType, sqlType, functionName, paramNumber); } break; case STRING: if ( !isCharacterType(code) && !isEnumType(code) ) { - throwError(type, javaType, functionName, paramNumber); + throwError(type, javaType, sqlType, functionName, paramNumber); } break; case STRING_OR_CLOB: if ( !isCharacterOrClobType(code) ) { - throwError(type, javaType, functionName, paramNumber); + throwError(type, javaType, sqlType, functionName, paramNumber); } break; case NUMERIC: if ( !isNumericType(code) ) { - throwError(type, javaType, functionName, paramNumber); + throwError(type, javaType, sqlType, functionName, paramNumber); } break; case INTEGER: if ( !isIntegral(code) ) { - throwError(type, javaType, functionName, paramNumber); + throwError(type, javaType, sqlType, functionName, paramNumber); } break; case BOOLEAN: // ugh, need to be careful here, need to accept all the // JDBC type codes that a Dialect might use for BOOLEAN if ( code != BOOLEAN && code != BIT && code != TINYINT && code != SMALLINT ) { - throwError(type, javaType, functionName, paramNumber); + throwError(type, javaType, sqlType, functionName, paramNumber); } break; case TEMPORAL: if ( !isTemporalType(code) ) { - throwError(type, javaType, functionName, paramNumber); + throwError(type, javaType, sqlType, functionName, paramNumber); } break; case DATE: if ( !hasDatePart(code) ) { - throwError(type, javaType, functionName, paramNumber); + throwError(type, javaType, sqlType, functionName, paramNumber); } break; case TIME: if ( !hasTimePart(code) ) { - throwError(type, javaType, functionName, paramNumber); + throwError(type, javaType, sqlType, functionName, paramNumber); } break; case SPATIAL: if ( !isSpatialType( code ) ) { - throwError( type, javaType, functionName, paramNumber ); + throwError( type, javaType, sqlType, functionName, paramNumber ); } } } - private static void throwError(FunctionParameterType type, Type javaType, String functionName, int paramNumber) { - throw new FunctionArgumentException( - String.format( - "Parameter %d of function '%s()' has type '%s', but argument is of type '%s'", - paramNumber, - functionName, - type, - javaType.getTypeName() - ) - ); + private static void throwError( + FunctionParameterType type, Type javaType, String sqlType, String functionName, int paramNumber) { + if ( sqlType == null ) { + throw new FunctionArgumentException( + String.format( + "Parameter %d of function '%s()' has type '%s', but argument is of type '%s'", + paramNumber, + functionName, + type, + javaType.getTypeName() + ) + ); + } + else { + throw new FunctionArgumentException( + String.format( + "Parameter %d of function '%s()' has type '%s', but argument is of type '%s' mapped to '%s'", + paramNumber, + functionName, + type, + javaType.getTypeName(), + sqlType + ) + ); + } } @Override From 2edd192b8d240a6e4640270cd4bc8e425aeef19a Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 19 Jan 2024 19:46:57 +0100 Subject: [PATCH 116/321] improve reporting of connection errors --- .../src/main/java/org/hibernate/JDBCException.java | 2 +- .../jdbc/connections/internal/BasicConnectionCreator.java | 5 +++-- .../jdbc/connections/internal/DriverConnectionCreator.java | 2 +- .../connections/internal/DriverManagerConnectionCreator.java | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/JDBCException.java b/hibernate-core/src/main/java/org/hibernate/JDBCException.java index 58b7c47dd556..1eca3b555f7d 100644 --- a/hibernate-core/src/main/java/org/hibernate/JDBCException.java +++ b/hibernate-core/src/main/java/org/hibernate/JDBCException.java @@ -42,7 +42,7 @@ public JDBCException(String message, SQLException cause) { * @param sql The sql being executed when the exception occurred */ public JDBCException(String message, SQLException cause, String sql) { - super( message + " [" + sql + "]", cause ); + super( sql == null ? message : message + " [" + sql + "]", cause ); this.message = message; this.sqlException = cause; this.sql = sql; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/BasicConnectionCreator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/BasicConnectionCreator.java index 24b3b3919455..84d1b1fe42ae 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/BasicConnectionCreator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/BasicConnectionCreator.java @@ -127,11 +127,12 @@ public JDBCException convert(SQLException sqlException, String message, String s ); protected JDBCException convertSqlException(String message, SQLException e) { + final String fullMessage = message + " [" + e.getMessage() + "]"; try { // if JdbcServices#getSqlExceptionHelper is available, use it... final JdbcServices jdbcServices = serviceRegistry.getService( JdbcServices.class ); if ( jdbcServices != null && jdbcServices.getSqlExceptionHelper() != null ) { - return jdbcServices.getSqlExceptionHelper().convert( e, message, null ); + return jdbcServices.getSqlExceptionHelper().convert( e, fullMessage ); } } catch (ServiceException se) { @@ -140,7 +141,7 @@ protected JDBCException convertSqlException(String message, SQLException e) { // likely we are still in the process of initializing the ServiceRegistry, so use the simplified // SQLException conversion - return simpleConverterAccess.getValue().convert( e, message, null ); + return simpleConverterAccess.getValue().convert( e, fullMessage, null ); } protected abstract Connection makeConnection(String url, Properties connectionProps); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverConnectionCreator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverConnectionCreator.java index 2f92a297a784..1f273c368d8f 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverConnectionCreator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverConnectionCreator.java @@ -40,7 +40,7 @@ protected Connection makeConnection(String url, Properties connectionProps) { return driver.connect( url, connectionProps ); } catch (SQLException e) { - throw convertSqlException( "Error calling Driver#connect", e ); + throw convertSqlException( "Error calling Driver.connect()", e ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionCreator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionCreator.java index c5e9a2341eb9..fe28c88d4352 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionCreator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionCreator.java @@ -36,7 +36,7 @@ protected Connection makeConnection(String url, Properties connectionProps) { return DriverManager.getConnection( url, connectionProps ); } catch (SQLException e) { - throw convertSqlException( "Error calling DriverManager#getConnection", e ); + throw convertSqlException( "Error calling DriverManager.getConnection()", e ); } } } From 9a8f02bd2149cae62237913d2ff1cd6bcd1d3342 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 19 Jan 2024 20:11:58 +0100 Subject: [PATCH 117/321] add some missing info to javadoc for @Find, @HQL, @SQL --- .../org/hibernate/annotations/processing/Find.java | 11 +++++++++-- .../org/hibernate/annotations/processing/HQL.java | 1 + .../org/hibernate/annotations/processing/SQL.java | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/processing/Find.java b/hibernate-core/src/main/java/org/hibernate/annotations/processing/Find.java index a610dc27c1a3..0a19f1b2d7d0 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/processing/Find.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/processing/Find.java @@ -74,7 +74,7 @@ * session object, instead of having a parameter of type * {@code EntityManager}, and *
  • the generated static metamodel class will actually implement - * the type which declares the method annotated {@code @SQL}. + * the type which declares the method annotated {@code @Find}. * *

    * Thus, the generated method may be called according to the following @@ -91,6 +91,7 @@ * or one of the following types: *

      *
    • {@link java.util.List java.util.List<E>}, + *
    • {@code io.smallrye.mutiny.Uni<E>}, when used with Hibernate Reactive, *
    • {@link org.hibernate.query.Query org.hibernate.query.Query<E>}, *
    • {@link org.hibernate.query.SelectionQuery org.hibernate.query.SelectionQuery<E>}, *
    • {@link jakarta.persistence.Query jakarta.persistence.Query<E>}, or @@ -105,8 +106,14 @@ * {@code @EmbeddedId} field of the entity, the finder method uses * {@link jakarta.persistence.EntityManager#find(Class, Object)} * to retrieve the entity. + *
    • Similarly, if there is one parameter, and its type matches the + * type of the {@link jakarta.persistence.IdClass IdClass} of the + * entity, the finder method uses + * {@link jakarta.persistence.EntityManager#find(Class, Object)} + * to retrieve the entity. In this case the parameter name is not + * significant. *
    • If the parameters match exactly with the {@code @NaturalId} - * field of the entity, the finder method uses + * field or fields of the entity, the finder method uses * {@link org.hibernate.Session#byNaturalId(Class)} to retrieve the * entity. *
    • Otherwise, the finder method builds and executes a diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/processing/HQL.java b/hibernate-core/src/main/java/org/hibernate/annotations/processing/HQL.java index 7772ea28c828..dd7a19f1a872 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/processing/HQL.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/processing/HQL.java @@ -80,6 +80,7 @@ *
        *
      • an entity type, *
      • {@link java.util.List}, + *
      • {@code io.smallrye.mutiny.Uni}, when used with Hibernate Reactive, *
      • {@link org.hibernate.query.Query}, *
      • {@link org.hibernate.query.SelectionQuery}, *
      • {@link jakarta.persistence.Query}, or diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/processing/SQL.java b/hibernate-core/src/main/java/org/hibernate/annotations/processing/SQL.java index 66e6e6d5d26e..618a21f7af30 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/processing/SQL.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/processing/SQL.java @@ -79,6 +79,7 @@ *
          *
        • an entity type, *
        • {@link java.util.List}, + *
        • {@code io.smallrye.mutiny.Uni}, when used with Hibernate Reactive, *
        • {@link org.hibernate.query.Query}, *
        • {@link jakarta.persistence.Query}, or *
        • {@link org.hibernate.query.NativeQuery}. From 971d31d9b04d4f5cb42301a962a235ff72f55f3e Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 22 Jan 2024 15:13:55 +0100 Subject: [PATCH 118/321] add a NOTE to make something clearer --- .../src/main/asciidoc/querylanguage/Expressions.adoc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/documentation/src/main/asciidoc/querylanguage/Expressions.adoc b/documentation/src/main/asciidoc/querylanguage/Expressions.adoc index fbe66e77eb20..df0aaf6ee31d 100644 --- a/documentation/src/main/asciidoc/querylanguage/Expressions.adoc +++ b/documentation/src/main/asciidoc/querylanguage/Expressions.adoc @@ -917,8 +917,6 @@ In case 2, application of the function produces an < Date: Fri, 2 Feb 2024 11:39:53 +0100 Subject: [PATCH 119/321] fix link in javadoc overview --- shared/javadoc/overview.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/javadoc/overview.html b/shared/javadoc/overview.html index d66f7722c69e..54759a5941e1 100644 --- a/shared/javadoc/overview.html +++ b/shared/javadoc/overview.html @@ -66,7 +66,7 @@

          JPA

        • {@link jakarta.persistence.TypedQuery} to execute queries,
        • {@link jakarta.persistence.EntityGraph} to control the boundaries of fetched data,
        • {@link jakarta.persistence.EntityTransaction} to control local transactions,
        • -
        • {@link jakarta.persistence.Metamodel} to implement generic code which makes use of +
        • {@link jakarta.persistence.metamodel.Metamodel} to implement generic code which makes use of persistent entity classes in a reflective fashion, and
        • {@link jakarta.persistence.criteria.CriteriaBuilder} to build JPA criteria queries.
        From 5c0486f840cd47519c7817c46ccd9a1f98e69511 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 2 Feb 2024 15:08:59 +0100 Subject: [PATCH 120/321] clarify use of Order and Page with @Find [It's allowed!] --- .../main/asciidoc/introduction/Generator.adoc | 18 ++++++++++++++++-- .../hibernate/annotations/processing/Find.java | 13 +++++++++++-- .../annotation/AnnotationMetaEntity.java | 17 +++++++++++++---- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/documentation/src/main/asciidoc/introduction/Generator.adoc b/documentation/src/main/asciidoc/introduction/Generator.adoc index 71979f113bcf..620ba4aab932 100644 --- a/documentation/src/main/asciidoc/introduction/Generator.adoc +++ b/documentation/src/main/asciidoc/introduction/Generator.adoc @@ -516,7 +516,7 @@ This lets us declare which associations of `Book` should be pre-fetched by annot [[paging-and-ordering]] === Paging and ordering -Optionally, a query method may have additional "magic" parameters which do not map to query parameters: +Optionally, a query method--or a finder method which returns multiple results--may have additional "magic" parameters which do not map to query parameters: [cols="19,~,32m"] |=== @@ -537,7 +537,8 @@ Thus, if we redefine our earlier query method as follows: ---- interface Queries { @HQL("from Book where title like :title and type = :type") - List findBooksByTitleAndType(String title, Page page, Order... order); + List findBooksByTitleAndType(String title, Type type, + Page page, Order... order); } ---- @@ -550,6 +551,19 @@ List books = Page.page(RESULTS_PER_PAGE, page), Order.asc(Book_.isbn)); ---- +Alternatively, we could have written this query method as a finder method: + +[source,java] +---- +interface Queries { + @Find + List getBooksByTitle(String title, Type type, + Page page, Order... order); +} +---- + +This gives some dynamic control over query execution, but what if would like direct control over the `Query` object? +Well, let's talk about the return type. [[return-types]] === Query and finder method return types diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/processing/Find.java b/hibernate-core/src/main/java/org/hibernate/annotations/processing/Find.java index 0a19f1b2d7d0..91642393b24f 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/processing/Find.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/processing/Find.java @@ -91,7 +91,7 @@ * or one of the following types: *
          *
        • {@link java.util.List java.util.List<E>}, - *
        • {@code io.smallrye.mutiny.Uni<E>}, when used with Hibernate Reactive, + *
        • {@code io.smallrye.mutiny.Uni}, when used with Hibernate Reactive, *
        • {@link org.hibernate.query.Query org.hibernate.query.Query<E>}, *
        • {@link org.hibernate.query.SelectionQuery org.hibernate.query.SelectionQuery<E>}, *
        • {@link jakarta.persistence.Query jakarta.persistence.Query<E>}, or @@ -123,7 +123,16 @@ *

          * As an exception, the method may have at most one parameter of * type {@code EntityManager}, {@code Session}, - * {@code StatelessSession}, or {@code Mutiny.Session}. + * {@code StatelessSession}, or {@code Mutiny.Session}. Furthermore, + * if the finder method returns multiple results, that is, if its + * return type is {@code List}, then it may also have: + *

            + *
          • a parameter with type {@code Page}, and/or + *
          • a parameter with type {@code Order}, + * {@code List>}, or {@code Order...} + * (varargs) where {@code E} is the entity type returned by the + * query. + *
          * * @see HQL * @see SQL diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java index eb7ceb277a09..adf16ee9bc56 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java @@ -545,6 +545,15 @@ private void addFinderMethod( createCriteriaFinder( method, returnType, containerType, entity ); } else { + for ( VariableElement parameter : method.getParameters() ) { + final String type = parameter.asType().toString(); + if ( isPageParam(type) ) { + context.message( parameter, "pagination would have no effect", Diagnostic.Kind.ERROR); + } + else if ( isOrderParam(type) ) { + context.message( parameter, "ordering would have no effect", Diagnostic.Kind.ERROR); + } + } final long parameterCount = method.getParameters().stream() .filter(AnnotationMetaEntity::isFinderParameterMappingToAttribute) @@ -570,8 +579,8 @@ private void addFinderMethod( private void createCriteriaFinder( ExecutableElement method, TypeMirror returnType, @Nullable TypeElement containerType, TypeElement entity) { final String methodName = method.getSimpleName().toString(); - final List paramNames = parameterNames(method); - final List paramTypes = parameterTypes(method); + final List paramNames = parameterNames( method ); + final List paramTypes = parameterTypes( method ); final String[] sessionType = sessionTypeFromParameters( paramNames, paramTypes ); final String methodKey = methodName + paramTypes; for ( VariableElement param : method.getParameters() ) { @@ -683,8 +692,8 @@ private void createSingleParameterFinder(ExecutableElement method, TypeMirror re method.getParameters().stream() .filter(AnnotationMetaEntity::isFinderParameterMappingToAttribute) .findFirst().orElseThrow(); - final List paramNames = parameterNames(method); - final List paramTypes = parameterTypes(method); + final List paramNames = parameterNames( method ); + final List paramTypes = parameterTypes( method ); final String[] sessionType = sessionTypeFromParameters( paramNames, paramTypes ); final FieldType fieldType = validateFinderParameter( entity, parameter ); if ( fieldType != null ) { From dfdf49dfa66766fbc5769ee00d6c8955d2622c55 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 3 Feb 2024 00:04:41 +0100 Subject: [PATCH 121/321] further improvements to jdoc of @Find and @HQL --- .../org/hibernate/annotations/processing/Find.java | 14 ++++++++++++++ .../org/hibernate/annotations/processing/HQL.java | 12 +++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/processing/Find.java b/hibernate-core/src/main/java/org/hibernate/annotations/processing/Find.java index 91642393b24f..9908855b028e 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/processing/Find.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/processing/Find.java @@ -47,6 +47,14 @@ * encode no semantics. *
        *

        + * It's even possible to query by a field of an embedded object: + *

        + * @Find
        + * List<Book> publishedBooks(String publisher$name);
        + * 
        + * Here, {@code publisher$name} refers to the field {@code name} of + * the {@code Book}'s {@code Publisher}. + *

        * The Metamodel Generator automatically creates an "implementation" * of every finder method in the static metamodel class {@code Books_}. * The generated method may be called according to the following @@ -133,6 +141,12 @@ * (varargs) where {@code E} is the entity type returned by the * query. *

      + *

      + * For example: + *

      + * @Find
      + * List<Book> getBooksWithTitle(String title, List<Order<Book>> order);
      + * 
      * * @see HQL * @see SQL diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/processing/HQL.java b/hibernate-core/src/main/java/org/hibernate/annotations/processing/HQL.java index dd7a19f1a872..5398b62ed3aa 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/processing/HQL.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/processing/HQL.java @@ -23,13 +23,13 @@ * For example: *
        * public interface Books {
      - *     @HQL("from Book where isbn = :isbn")
      + *     @HQL("where isbn = :isbn")
        *     Book findBookByIsbn(String isbn);
        *
      - *     @HQL("from Book where title like ?1 order by title offset ?3 fetch first ?2 rows only")
      + *     @HQL("where title like ?1 order by title offset ?3 fetch first ?2 rows only")
        *     List<Book> findBooksByTitleWithPagination(String title, int max, int start);
        *
      - *     @HQL("from Book where title like ?1")
      + *     @HQL("where title like ?1")
        *     TypedQuery<Book> createBooksByTitleQuery(String title);
        * }
        * 
      @@ -111,6 +111,12 @@ * query. *
    *

    + * For example: + *

    + * @HQL("where title like :title)
    + * List<Book> findBooksByTitleWithPagination(String title, Page page, Order<Book> order);
    + * 
    + *

    * Queries specified using this annotation are always validated by * the Metamodel Generator, and so it isn't necessary to specify the * {@link CheckHQL} annotation. From 2e360027b5c8e44e608a96330782b079df092690 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 3 Feb 2024 00:11:43 +0100 Subject: [PATCH 122/321] small fixes to @Find and @HQL methods don't include session parameter type where not necessary --- .../annotation/AbstractFinderMethod.java | 49 +++++++++++-------- .../jpamodelgen/annotation/QueryMethod.java | 3 +- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AbstractFinderMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AbstractFinderMethod.java index 3cb9afd564e7..658562dc8a0a 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AbstractFinderMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AbstractFinderMethod.java @@ -88,28 +88,37 @@ void comment(StringBuilder declaration) { .append("{@link ") .append(annotationMetaEntity.importType(entity)) .append("} by "); - int paramCount = paramNames.size(); - for (int i = 0; i < paramCount; i++) { - String param = paramNames.get(i); - if ( i>0 ) { - if ( i + 1 == paramCount) { - declaration - .append(paramCount>2 ? ", and " : " and "); //Oxford comma - } - else { - declaration - .append(", "); + long paramCount = paramTypes.stream() + .filter(type -> !isOrderParam(type) && !isPageParam(type) + && !isSessionParameter(type)) + .count(); + int count = 0; + for (int i = 0; i < paramTypes.size(); i++) { + String type = paramTypes.get(i); + if ( !isPageParam(type) && !isOrderParam(type) + && !isSessionParameter(type) ) { + if ( count>0 ) { + if ( count + 1 == paramCount) { + declaration + .append(paramCount>2 ? ", and " : " and "); //Oxford comma + } + else { + declaration + .append(", "); + } } + count++; + final String path = paramNames.get(i) + .replace('$', '.'); + declaration + .append("{@link ") + .append(annotationMetaEntity.importType(entity)) + .append('#') + .append(qualifier(path)) + .append(' ') + .append(path) + .append("}"); } - final String path = param.replace('$', '.'); - declaration - .append("{@link ") - .append(annotationMetaEntity.importType(entity)) - .append('#') - .append(qualifier(path)) - .append(' ') - .append(path) - .append("}"); } declaration .append('.') diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/QueryMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/QueryMethod.java index e4c5908e0c01..a1a439c5c73a 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/QueryMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/QueryMethod.java @@ -254,7 +254,8 @@ private String getConstantName() { else { return stem + "_" + paramTypes.stream() - .filter(name -> !isPageParam(name) && !isOrderParam(name)) + .filter(type -> !isPageParam(type) && !isOrderParam(type) + && !isSessionParameter(type)) .map(StringHelper::unqualify) .reduce((x,y) -> x + '_' + y) .orElse(""); From c6367d0f4568ceeb85b28e569977212334b8d99b Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 3 Feb 2024 13:39:53 +0100 Subject: [PATCH 123/321] improve preamble of Generator.adoc --- .../src/main/asciidoc/introduction/Generator.adoc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/documentation/src/main/asciidoc/introduction/Generator.adoc b/documentation/src/main/asciidoc/introduction/Generator.adoc index 620ba4aab932..9675579b39de 100644 --- a/documentation/src/main/asciidoc/introduction/Generator.adoc +++ b/documentation/src/main/asciidoc/introduction/Generator.adoc @@ -130,9 +130,14 @@ The Metamodel Generator is not currently able to generate finder methods and que We're going to meet three different kinds of generated method: -- a _named query method_ has its signature and implementation generated directly from a `@NamedQuery` annotation, -- a _query method_ has a signature that's explicitly declared, and a generated implementation which executes a HQL or SQL query specified via a `@HQL` or `@SQL` annotation, and -- a _finder method_ annotated `@Find` has a signature that's explicitly declared, and a generated implementation inferred from the parameter list. +- a _<>_ has its signature and implementation generated directly from a `@NamedQuery` annotation, +- a _<>_ has a signature that's explicitly declared, and a generated implementation which executes a HQL or SQL query specified via a `@HQL` or `@SQL` annotation, and +- a _<>_ annotated `@Find` has a signature that's explicitly declared, and a generated implementation inferred from the parameter list. + +We're also going to see two ways that these methods can be called: + +- as static methods of a generated abstract class, or +- as <> with a generated implementation which may even be <>. To whet our appetites, let's see how this works for a `@NamedQuery`. @@ -355,6 +360,7 @@ interface Queries { What if we would like to inject a `Queries` object instead of calling its constructor directly? +[[cdi-bean-injection]] [%unbreakable] [TIP] ==== From da04eb7736d8564db829a13a067df34b55803074 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 3 Feb 2024 13:51:27 +0100 Subject: [PATCH 124/321] improve format of error messages relating to getters/setters --- .../internal/util/ReflectHelper.java | 21 +++++++++---------- .../access/internal/AccessStrategyHelper.java | 7 +++---- .../property/GetAndIsVariantGetterTest.java | 3 ++- .../MissingSetterWithEnhancementTest.java | 4 ++-- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/ReflectHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/ReflectHelper.java index 38a7b5756119..0e0b66ba6ae7 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/ReflectHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/ReflectHelper.java @@ -485,9 +485,9 @@ public static Method findGetterMethod(Class containerClass, String propertyName) throw new PropertyNotFoundException( String.format( Locale.ROOT, - "Could not locate getter method for property [%s#%s]", - containerClass.getName(), - propertyName + "Could not locate getter method for property '%s' of class '%s'", + propertyName, + containerClass.getName() ) ); } @@ -608,12 +608,11 @@ public static void checkGetAndIsVariants( throw new MappingException( String.format( Locale.ROOT, - "In trying to locate getter for property [%s], Class [%s] defined " + - "both a `get` [%s] and `is` [%s] variant", - propertyName, + "Class '%s' declares both 'get' [%s] and 'is' [%s] variants of getter for property '%s'", containerClass.getName(), - getMethod.toString(), - isMethod.toString() + getMethod, + isMethod, + propertyName ) ); } @@ -731,9 +730,9 @@ public static Method findSetterMethod(final Class containerClass, final String p throw new PropertyNotFoundException( String.format( Locale.ROOT, - "Could not locate setter method for property [%s#%s]", - containerClass.getName(), - propertyName + "Could not locate setter method for property '%s' of class '%s'", + propertyName, + containerClass.getName() ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/AccessStrategyHelper.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/AccessStrategyHelper.java index db40f8e48006..2e549b960f80 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/internal/AccessStrategyHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/AccessStrategyHelper.java @@ -152,12 +152,11 @@ private static void checkIsMethodVariant( throw new MappingException( String.format( Locale.ROOT, - "In trying to locate getter for property [%s], Class [%s] defined " + - "both a `get` [%s] and `is` [%s] variant", - propertyName, + "Class '%s' declares both 'get' [%s] and 'is' [%s] variants of getter for property '%s'", containerClass.getName(), method.toString(), - isMethodVariant.toString() + isMethodVariant, + propertyName ) ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/property/GetAndIsVariantGetterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/property/GetAndIsVariantGetterTest.java index 4c3ceeef0efc..aa9872e8fefb 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/property/GetAndIsVariantGetterTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/property/GetAndIsVariantGetterTest.java @@ -25,6 +25,7 @@ import org.junit.BeforeClass; import org.junit.Test; +import static org.hamcrest.CoreMatchers.endsWith; import static org.hamcrest.CoreMatchers.startsWith; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; @@ -61,7 +62,7 @@ public void testHbmXml() { fail( "Expecting a failure" ); } catch (MappingException e) { - assertThat( e.getMessage(), startsWith( "In trying to locate getter for property [id]" ) ); + assertThat( e.getMessage(), endsWith( "variants of getter for property 'id'" ) ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/MissingSetterWithEnhancementTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/MissingSetterWithEnhancementTest.java index 41b2ab53d652..d48b34eb9907 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/MissingSetterWithEnhancementTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/MissingSetterWithEnhancementTest.java @@ -13,7 +13,6 @@ import org.hibernate.MappingException; import org.hibernate.SessionFactory; import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; -import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import org.hibernate.service.ServiceRegistry; @@ -64,7 +63,8 @@ public void testEnhancedClassMissesSetterForProperty() { } catch (MappingException e) { assertEquals( - "Could not locate setter method for property [" + EntityWithMissingSetter.class.getName() + "#name]", + "Could not locate setter method for property 'name' of class '" + + EntityWithMissingSetter.class.getName() + "'", e.getMessage() ); } From 6fef20cd49295e01a19a3381f2c474b3114ea0f5 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 6 Feb 2024 12:33:10 +0100 Subject: [PATCH 125/321] better handling of the session variable in repositories --- .../annotation/AnnotationMeta.java | 5 +++ .../annotation/AnnotationMetaEntity.java | 26 +++++++++++++-- .../annotation/DaoConstructor.java | 33 +++++++++++++------ .../annotation/NamedQueryMethod.java | 10 ++++-- 4 files changed, 60 insertions(+), 14 deletions(-) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMeta.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMeta.java index 45e7981b5d8b..4c98aea8a109 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMeta.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMeta.java @@ -112,6 +112,7 @@ && isQueryMethodName( name ) ) { name.substring(1), belongsToDao(), getSessionType(), + getSessionVariableName(), getContext().addNonnullAnnotation() ) ); @@ -162,6 +163,10 @@ private void addAuxiliaryMembersForMirror(AnnotationMirror mirror, String prefix }); } + protected String getSessionVariableName() { + return "entityManager"; + } + abstract boolean belongsToDao(); abstract @Nullable String getSessionType(); diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java index adf16ee9bc56..039beac2da6c 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java @@ -58,6 +58,9 @@ import static org.hibernate.internal.util.StringHelper.qualify; import static org.hibernate.jpamodelgen.annotation.QueryMethod.isOrderParam; import static org.hibernate.jpamodelgen.annotation.QueryMethod.isPageParam; +import static org.hibernate.jpamodelgen.util.Constants.HIB_SESSION; +import static org.hibernate.jpamodelgen.util.Constants.HIB_STATELESS_SESSION; +import static org.hibernate.jpamodelgen.util.Constants.MUTINY_SESSION; import static org.hibernate.jpamodelgen.util.Constants.SESSION_TYPES; import static org.hibernate.jpamodelgen.util.NullnessUtil.castNonNull; import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation; @@ -342,6 +345,7 @@ private String addDaoConstructor(ExecutableElement method) { typeName, name, sessionType, + getSessionVariableName(sessionType), context.addInjectAnnotation(), context.addNonnullAnnotation() ) @@ -429,7 +433,9 @@ private boolean isPersistent(Element memberOfClass, AccessType membersKind) { || determineAnnotationSpecifiedAccessType( memberOfClass ) != null ) && !containsAnnotation( memberOfClass, Constants.TRANSIENT ) && !memberOfClass.getModifiers().contains( Modifier.TRANSIENT ) - && !memberOfClass.getModifiers().contains( Modifier.STATIC ); + && !memberOfClass.getModifiers().contains( Modifier.STATIC ) + && !( memberOfClass.getKind() == ElementKind.METHOD + && isSessionGetter( (ExecutableElement) memberOfClass ) ); } private void addQueryMethods(List queryMethods) { @@ -621,7 +627,23 @@ private String[] sessionTypeFromParameters(List paramNames, List return new String[] { type, name }; } } - return new String[] { sessionType, "entityManager" }; + return new String[] { getSessionType(), getSessionVariableName() }; + } + + @Override + protected String getSessionVariableName() { + return getSessionVariableName(sessionType); + } + + private static String getSessionVariableName(String sessionType) { + switch (sessionType) { + case HIB_SESSION: + case HIB_STATELESS_SESSION: + case MUTINY_SESSION: + return "session"; + default: + return "entityManager"; + } } private static List enabledFetchProfiles(ExecutableElement method) { diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/DaoConstructor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/DaoConstructor.java index 6487a85f7ad7..87f17d8d2215 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/DaoConstructor.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/DaoConstructor.java @@ -17,7 +17,8 @@ public class DaoConstructor implements MetaAttribute { private final Metamodel annotationMetaEntity; private final String constructorName; private final String methodName; - private final String returnTypeName; + private final String sessionTypeName; + private final String sessionVariableName; private final boolean addInjectAnnotation; private final boolean addNonnullAnnotation; @@ -25,13 +26,15 @@ public DaoConstructor( Metamodel annotationMetaEntity, String constructorName, String methodName, - String returnTypeName, + String sessionTypeName, + String sessionVariableName, boolean addInjectAnnotation, boolean addNonnullAnnotation) { this.annotationMetaEntity = annotationMetaEntity; this.constructorName = constructorName; this.methodName = methodName; - this.returnTypeName = returnTypeName; + this.sessionTypeName = sessionTypeName; + this.sessionVariableName = sessionVariableName; this.addInjectAnnotation = addInjectAnnotation; this.addNonnullAnnotation = addNonnullAnnotation; } @@ -53,8 +56,10 @@ public String getAttributeDeclarationString() { .append("\nprivate final "); notNull( declaration ); declaration - .append(annotationMetaEntity.importType(returnTypeName)) - .append(" entityManager;") + .append(annotationMetaEntity.importType(sessionTypeName)) + .append(" ") + .append(sessionVariableName) + .append(";") .append("\n"); inject( declaration ); declaration @@ -63,19 +68,27 @@ public String getAttributeDeclarationString() { .append("("); notNull( declaration ); declaration - .append(annotationMetaEntity.importType(returnTypeName)) - .append(" entityManager) {") - .append("\n\tthis.entityManager = entityManager;") + .append(annotationMetaEntity.importType(sessionTypeName)) + .append(" ") + .append(sessionVariableName) + .append(") {") + .append("\n\tthis.") + .append(sessionVariableName) + .append(" = ") + .append(sessionVariableName) + .append(";") .append("\n}") .append("\n\n") .append("public "); notNull( declaration ); declaration - .append(annotationMetaEntity.importType(returnTypeName)) + .append(annotationMetaEntity.importType(sessionTypeName)) .append(" ") .append(methodName) .append("() {") - .append("\n\treturn entityManager;") + .append("\n\treturn ") + .append(sessionVariableName) + .append(";") .append("\n}"); return declaration.toString(); } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/NamedQueryMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/NamedQueryMethod.java index dde6dcb709b4..851f5627142f 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/NamedQueryMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/NamedQueryMethod.java @@ -33,6 +33,7 @@ class NamedQueryMethod implements MetaAttribute { private final String name; private final boolean belongsToDao; private final boolean reactive; + private final String sessionVariableName; private final boolean addNonnullAnnotation; public NamedQueryMethod( @@ -41,12 +42,14 @@ public NamedQueryMethod( String name, boolean belongsToDao, @Nullable String sessionType, + String sessionVariableName, boolean addNonnullAnnotation) { this.annotationMeta = annotationMeta; this.select = select; this.name = name; this.belongsToDao = belongsToDao; this.reactive = Constants.MUTINY_SESSION.equals(sessionType); + this.sessionVariableName = sessionVariableName; this.addNonnullAnnotation = addNonnullAnnotation; } @@ -71,7 +74,9 @@ public String getAttributeDeclarationString() { parameters( sortedParameters, declaration ); declaration .append(" {") - .append("\n\treturn entityManager.createNamedQuery(") + .append("\n\treturn ") + .append(sessionVariableName) + .append(".createNamedQuery(") .append(fieldName()) .append(")"); for ( SqmParameter param : sortedParameters ) { @@ -159,7 +164,8 @@ private void parameters(TreeSet> sortedParameters, StringBuilder notNull( declaration ); declaration .append(annotationMeta.importType(Constants.ENTITY_MANAGER)) - .append(" entityManager"); + .append(" ") + .append(sessionVariableName); } int i = 0; for ( SqmParameter param : sortedParameters) { From d36131eff520d1e3e4aa2e929a2a0d9d6a8e29e2 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 6 Feb 2024 15:05:56 +0100 Subject: [PATCH 126/321] big code example in package doc for org.hibernate.annotations.processing --- .../annotations/processing/package-info.java | 102 +++++++++++++++++- 1 file changed, 100 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/processing/package-info.java b/hibernate-core/src/main/java/org/hibernate/annotations/processing/package-info.java index c47518324215..76ebc5a2e6a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/processing/package-info.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/processing/package-info.java @@ -8,14 +8,112 @@ /** * Annotations used to drive annotation processors: *

      + *
    • {@link org.hibernate.annotations.processing.Find @Find} + * is used to generate finder methods using the Metamodel + * Generator, *
    • {@link org.hibernate.annotations.processing.HQL @HQL} * and {@link org.hibernate.annotations.processing.SQL @SQL} - * are used to generate query methods using the - * Metamodel Generator, and + * are used to generate query methods using the Metamodel + * Generator, and *
    • {@link org.hibernate.annotations.processing.CheckHQL} * instructs the Query Validator to check all HQL queries * in the annotated package or type. *
    + *

    + * Annotations in this package control Hibernate's compile-time + * tooling. For example, this code defines a DAO-style repository + * object: + *

    + * package org.example;
    + *
    + * import org.hibernate.StatelessSession;
    + * import org.hibernate.annotations.processing.Find;
    + * import org.hibernate.annotations.processing.HQL;
    + * import org.hibernate.query.Order;
    + *
    + * import java.util.List;
    + *
    + * interface BookRepository {
    + *
    + *   StatelessSession session();
    + *
    + *   @Find
    + *   Book bookByIsbn(String isbn);
    + *
    + *   @Find
    + *   List<Book> booksByPublisher(long publisher$id);
    + *
    + *   @HQL("where title like :title")
    + *   List<Book> booksByTitle(String title, Order<Book> order);
    + *
    + * }
    + * 
    + *

    + * The Metamodel Generator produces this implementation when + * the interface is compiled: + *

    + * package org.example;
    + *
    + * import jakarta.annotation.Generated;
    + * import jakarta.annotation.Nonnull;
    + * import jakarta.enterprise.context.Dependent;
    + * import jakarta.inject.Inject;
    + * import jakarta.persistence.metamodel.StaticMetamodel;
    + * import java.util.List;
    + * import org.hibernate.StatelessSession;
    + * import org.hibernate.query.Order;
    + *
    + * @Dependent 
    + * @StaticMetamodel(BookRepository.class) 
    + * @Generated("org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor") 
    + * public class BookRepository_ implements BookRepository {
    + *
    + *
    + *    @Override 
    + *    public List<Book> booksByTitle(String title, Order<Book> order) {
    + *         return session.createQuery(BOOKS_BY_TITLE_String, Book.class)
    + *                 .setParameter("title", title)
    + *                 .setOrder(order)
    + *                 .getResultList();
    + *    }
    + *
    + *    @Override 
    + *    public Book bookByIsbn(@Nonnull String isbn) {
    + *         return session.get(Book.class, isbn);
    + *    }
    + *
    + *     private final @Nonnull StatelessSession session;
    + *
    + *    @Inject 
    + *    public BookRepository_(@Nonnull StatelessSession session) {
    + *         this.session = session;
    + *    }
    + *
    + *    public @Nonnull StatelessSession session() {
    + *         return session;
    + *    }
    + *
    + *    @Override 
    + *    public List<Book> booksByPublisher(long publisher$id) {
    + *         var builder = session.getFactory().getCriteriaBuilder();
    + *         var query = builder.createQuery(Book.class);
    + *         var entity = query.from(Book.class);
    + *         query.where(
    + *                 builder.equal(entity.get(Book_.publisher).get(Publisher_.id), publisher$id)
    + *         );
    + *         return session.createQuery(query).getResultList();
    + *    }
    + *
    + *     static final String BOOKS_BY_TITLE_String = "where title like :title";
    + *
    + * }
    + * 
    + *

    + * The exact annotations included in the generated code depend on + * the libraries available during compilation. The code above was + * produced with CDI and Jakarta annotations on the build path, + * and so the repository is an injectable CDI bean and uses + * {@code @Nonnull} to indicate required parameters. */ @Incubating package org.hibernate.annotations.processing; From ee3334778636b0c0a8ce4bec838bdefb65e094de Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 6 Feb 2024 17:04:20 +0100 Subject: [PATCH 127/321] HHH-17716 implement JtaTransactionAdapterTransactionManagerImpl.setTimeOut() --- .../JtaTransactionAdapterTransactionManagerImpl.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionAdapterTransactionManagerImpl.java b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionAdapterTransactionManagerImpl.java index b5f0b9dd3ad3..7f64f6e57b95 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionAdapterTransactionManagerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionAdapterTransactionManagerImpl.java @@ -106,6 +106,13 @@ public void markRollbackOnly() { @Override public void setTimeOut(int seconds) { - + if ( seconds > 0 ) { + try { + transactionManager.setTransactionTimeout( seconds ); + } + catch (SystemException e) { + throw new TransactionException( "Unable to apply requested transaction timeout", e ); + } + } } } From c8d13108b5ebd9249b29d64574ffd8f89b125645 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 6 Feb 2024 20:31:46 +0100 Subject: [PATCH 128/321] document Transaction.setTimeout() --- .../src/main/asciidoc/introduction/Interacting.adoc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/documentation/src/main/asciidoc/introduction/Interacting.adoc b/documentation/src/main/asciidoc/introduction/Interacting.adoc index 4f64b4e5cc6a..220219786a33 100644 --- a/documentation/src/main/asciidoc/introduction/Interacting.adoc +++ b/documentation/src/main/asciidoc/introduction/Interacting.adoc @@ -207,6 +207,13 @@ In a container environment, the container itself is usually responsible for mana In Java EE or Quarkus, you'll probably indicate the boundaries of the transaction using the `@Transactional` annotation. **** +JPA doesn't have a standard way to set the transaction timeout, but Hibernate does: + +[source,java] +---- +session.getTransaction().setTimeout(30); // 30 seconds +---- + [[persistence-operations]] === Operations on the persistence context From 594fd2f4aa8f9955ed9d8f3497d8b187d91308c2 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 7 Feb 2024 10:10:49 +0100 Subject: [PATCH 129/321] add missing package-info --- .../backend/jta/internal/package-info.java | 16 ++++++++++++++++ .../resource/transaction/spi/package-info.java | 3 +++ 2 files changed, 19 insertions(+) create mode 100644 hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/package-info.java diff --git a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/package-info.java b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/package-info.java new file mode 100644 index 000000000000..894b7d136cd4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/package-info.java @@ -0,0 +1,16 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +/** + * Implementations of {@link org.hibernate.resource.transaction.spi.TransactionCoordinator} + * based on JTA. + *

    + * The {@link org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionAdapter} + * abstracts over access to JTA via {@link jakarta.transaction.UserTransaction} or via + * {@link jakarta.transaction.TransactionManager}. + */ +package org.hibernate.resource.transaction.backend.jta.internal; diff --git a/hibernate-core/src/main/java/org/hibernate/resource/transaction/spi/package-info.java b/hibernate-core/src/main/java/org/hibernate/resource/transaction/spi/package-info.java index 6439b3b77135..cb1bcf0f3654 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/transaction/spi/package-info.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/transaction/spi/package-info.java @@ -7,5 +7,8 @@ /** * Extended SPI contracts for the resource-level transaction capabilities of Hibernate. + * Underlies the {@link org.hibernate.Transaction} API. + * + * @see org.hibernate.resource.transaction.spi.TransactionCoordinator */ package org.hibernate.resource.transaction.spi; From 8346b7132df67e608cac1af71f83c0930530fea5 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 7 Feb 2024 11:48:51 +0100 Subject: [PATCH 130/321] more blurb about finder/query methods --- .../annotations/processing/package-info.java | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/processing/package-info.java b/hibernate-core/src/main/java/org/hibernate/annotations/processing/package-info.java index 76ebc5a2e6a7..367d3b0ac7f0 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/processing/package-info.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/processing/package-info.java @@ -21,8 +21,31 @@ * *

    * Annotations in this package control Hibernate's compile-time - * tooling. For example, this code defines a DAO-style repository - * object: + * tooling, and depend on the use of the annotation processors + * in the {@code hibernate-jpamodelgen} or {@code query-validator} + * modules. If the appropriate annotation processor is not enabled + * at build time, these annotations have no effect. + *

    + * Finder methods and query methods are usually declared by an + * interface, say {@code Queries}, and their implementations + * are generated into a class whose name follows the convention + * of the JPA static metamodel, that is, {@code Queries_}. + *

      + *
    • If the interface declares a method which returns an + * {@code EntityManager}, Hibernate session, stateless + * session, or reactive session, the generated class + * implements the interface, {@code class Queries_ implements + * Queries}, the generated methods are instance methods, and + * the session must be provided to the class constructor, + * possibly via dependency injection. + *
    • Otherwise, if there is no such method, the generated class + * is non-instantiable, {@code abstract class Queries_}, the + * generated methods are declared {@code static}, and the + * session must be provided as an argument by the caller of + * the generated methods. + *
    + *

    + * For example, this code defines a DAO-style repository object: *

      * package org.example;
      *
    
    From 3cb5c072745cc621e402f500f53ea36f7935fb96 Mon Sep 17 00:00:00 2001
    From: Gavin King 
    Date: Wed, 7 Feb 2024 13:20:23 +0100
    Subject: [PATCH 131/321] validate parameters of @SQL query
    
    ---
     .../annotation/AnnotationMetaEntity.java      | 54 ++++++++++++++++++-
     1 file changed, 52 insertions(+), 2 deletions(-)
    
    diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java
    index 039beac2da6c..3a8b4210d9c6 100644
    --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java
    +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java
    @@ -44,6 +44,8 @@
     import org.hibernate.query.criteria.JpaEntityJoin;
     import org.hibernate.query.criteria.JpaRoot;
     import org.hibernate.query.criteria.JpaSelection;
    +import org.hibernate.query.sql.internal.ParameterParser;
    +import org.hibernate.query.sql.spi.ParameterRecognizer;
     import org.hibernate.query.sqm.SqmExpressible;
     import org.hibernate.query.sqm.tree.SqmStatement;
     import org.hibernate.query.sqm.tree.expression.SqmParameter;
    @@ -995,10 +997,12 @@ private void addQueryMethod(
     						);
     				putMember( attribute.getPropertyName() + paramTypes, attribute );
     
    -				if ( !isNative ) {
    +				if ( isNative ) {
    +					validateSql( method, mirror, hql, paramNames, value );
    +				}
    +				else {
     					validateHql( method, returnType, mirror, value, hql, paramNames, paramTypes );
     				}
    -				//TODO: for SQL queries check that there is a method parameter for every query parameter
     
     				// now check that the query has a parameter for every method parameter
     				checkParameters( method, paramNames, paramTypes, mirror, value, hql );
    @@ -1111,6 +1115,52 @@ else if ( selection instanceof JpaRoot ) {
     		}
     	}
     
    +	private void validateSql(
    +			ExecutableElement method,
    +			AnnotationMirror mirror,
    +			String hql,
    +			List paramNames,
    +			AnnotationValue value) {
    +		// for SQL queries check that there is a method parameter for every query parameter
    +		ParameterParser.parse(hql, new ParameterRecognizer() {
    +			int ordinalCount = 0;
    +			@Override
    +			public void ordinalParameter(int sourcePosition) {
    +				ordinalCount++;
    +				if ( ordinalCount > paramNames.size() ) {
    +					context.message(method, mirror, value,
    +							"missing method parameter for query parameter " + ordinalCount
    +									+ " (add a parameter to '" + method.getSimpleName() + "')",
    +							Diagnostic.Kind.ERROR );
    +				}
    +			}
    +
    +			@Override
    +			public void namedParameter(String name, int sourcePosition) {
    +				if ( !paramNames.contains(name) ) {
    +					context.message(method, mirror, value,
    +							"missing method parameter for query parameter :" + name
    +									+ " (add a parameter '" + name + "' to '" + method.getSimpleName() + "')",
    +							Diagnostic.Kind.ERROR );
    +				}
    +			}
    +
    +			@Override
    +			public void jpaPositionalParameter(int label, int sourcePosition) {
    +				if ( label > paramNames.size() ) {
    +					context.message(method, mirror, value,
    +							"missing method parameter for query parameter ?" + label
    +									+ " (add a parameter to '" + method.getSimpleName() + "')",
    +							Diagnostic.Kind.ERROR );
    +				}
    +			}
    +
    +			@Override
    +			public void other(char character) {
    +			}
    +		});
    +	}
    +
     	private static boolean checkConstructorReturn(DeclaredType returnType, JpaSelection selection) {
     		final List> selectionItems = selection.getSelectionItems();
     		if ( selectionItems == null ) {
    
    From d6390ce79f45b65d054558517ad463b0da3e6c61 Mon Sep 17 00:00:00 2001
    From: Yanming Zhou 
    Date: Wed, 7 Feb 2024 17:19:27 +0800
    Subject: [PATCH 132/321] HHH-17719 Supports boolean as return type of mutation
     query method
    
    ---
     .../src/main/asciidoc/introduction/Generator.adoc         | 8 +++++++-
     .../java/org/hibernate/annotations/processing/HQL.java    | 5 +++--
     .../jpamodelgen/annotation/AnnotationMetaEntity.java      | 4 +++-
     .../org/hibernate/jpamodelgen/annotation/QueryMethod.java | 6 +++++-
     .../test/java/org/hibernate/jpamodelgen/test/dao/Dao.java | 3 +++
     5 files changed, 21 insertions(+), 5 deletions(-)
    
    diff --git a/documentation/src/main/asciidoc/introduction/Generator.adoc b/documentation/src/main/asciidoc/introduction/Generator.adoc
    index 9675579b39de..4754fae01d77 100644
    --- a/documentation/src/main/asciidoc/introduction/Generator.adoc
    +++ b/documentation/src/main/asciidoc/introduction/Generator.adoc
    @@ -662,7 +662,7 @@ List books =
     //                 })
     // ----
     
    -An `insert`, `update`, or `delete` query must return `int` or `void`.
    +An `insert`, `update`, or `delete` query must return `int`, `boolean`, or `void`.
     
     [source,java]
     ----
    @@ -670,6 +670,12 @@ An `insert`, `update`, or `delete` query must return `int` or `void`.
     int deleteAllBooks();
     ----
     
    +[source,java]
    +----
    +@HQL("update Book set discontinued = true where discontinued = false and isbn = :isbn")
    +boolean discontinueBook(String isbn);
    +----
    +
     [source,java]
     ----
     @HQL("update Book set discontinued = true where isbn = :isbn")
    diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/processing/HQL.java b/hibernate-core/src/main/java/org/hibernate/annotations/processing/HQL.java
    index 5398b62ed3aa..a5d106943719 100644
    --- a/hibernate-core/src/main/java/org/hibernate/annotations/processing/HQL.java
    +++ b/hibernate-core/src/main/java/org/hibernate/annotations/processing/HQL.java
    @@ -88,8 +88,8 @@
      * 
      * 

    * For an {@code insert}, {@code update}, or {@code delete} query, - * the return type of the annotated method must be {@code int} or - * {@code void}. + * the return type of the annotated method must be {@code int}, + * {@code boolean}, or {@code void}. *

    * The method parameters must match the parameters of the HQL query, * either by name or by position: @@ -125,6 +125,7 @@ * @see Find * * @author Gavin King + * @author Yanming Zhou * @since 6.3 */ @Target(METHOD) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java index 3a8b4210d9c6..c889b1fc28db 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java @@ -83,6 +83,7 @@ * @author Hardy Ferentschik * @author Emmanuel Bernard * @author Gavin King + * @author Yanming Zhou */ public class AnnotationMetaEntity extends AnnotationMeta { @@ -1053,9 +1054,10 @@ private void validateUpdateHql( AnnotationValue value) { if ( returnType == null || returnType.getKind() != TypeKind.VOID + && returnType.getKind() != TypeKind.BOOLEAN && returnType.getKind() != TypeKind.INT ) { context.message( method, mirror, value, - "return type of mutation query method must be 'int' or 'void'", + "return type of mutation query method must be 'int', 'boolean', or 'void'", Diagnostic.Kind.ERROR ); } } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/QueryMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/QueryMethod.java index a1a439c5c73a..8fa7f972cc53 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/QueryMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/QueryMethod.java @@ -16,6 +16,7 @@ /** * @author Gavin King + * @author Yanming Zhou */ public class QueryMethod extends AbstractQueryMethod { private final String queryString; @@ -116,8 +117,11 @@ private void execute(StringBuilder declaration, boolean unwrapped) { if ( isUpdate ) { declaration .append("\n\t\t\t.executeUpdate()"); + if ( "boolean".equals(returnTypeName) ) { + declaration.append(" > 0"); + } } - else if ( containerTypeName == null) { + else if ( containerTypeName == null ) { declaration .append("\n\t\t\t.getSingleResult()"); } diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/dao/Dao.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/dao/Dao.java index 0e57bee5d60f..56d95ff58795 100644 --- a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/dao/Dao.java +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/dao/Dao.java @@ -74,6 +74,9 @@ public interface Dao { @HQL("delete from Book") int deleteBooks(); + @HQL("delete from Book book where book.isbn=:isbn") + boolean deleteBook(String isbn); + @HQL("select count(*), count(*)>1 from Book") Object[] funnyQueryReturningArray(); From 015f3fd1d2e8eb731b9701a8296a56bf1d3b3cc9 Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Wed, 7 Feb 2024 21:56:51 +0800 Subject: [PATCH 133/321] Rename variable for better readability --- .../annotation/AnnotationMetaEntity.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java index c889b1fc28db..80abe9c981b5 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java @@ -976,7 +976,7 @@ private void addQueryMethod( if ( value != null ) { final Object query = value.getValue(); if ( query instanceof String ) { - final String hql = (String) query; + final String queryString = (String) query; final List paramNames = parameterNames( method ); final List paramTypes = parameterTypes( method ); final String[] sessionType = sessionTypeFromParameters( paramNames, paramTypes ); @@ -984,12 +984,12 @@ private void addQueryMethod( new QueryMethod( this, method.getSimpleName().toString(), - hql, + queryString, returnType == null ? null : returnType.toString(), containerType == null ? null : containerType.getQualifiedName().toString(), paramNames, paramTypes, - isInsertUpdateDelete( hql ), + isInsertUpdateDelete( queryString ), isNative, dao, sessionType[0], @@ -999,14 +999,14 @@ private void addQueryMethod( putMember( attribute.getPropertyName() + paramTypes, attribute ); if ( isNative ) { - validateSql( method, mirror, hql, paramNames, value ); + validateSql( method, mirror, queryString, paramNames, value ); } else { - validateHql( method, returnType, mirror, value, hql, paramNames, paramTypes ); + validateHql( method, returnType, mirror, value, queryString, paramNames, paramTypes ); } // now check that the query has a parameter for every method parameter - checkParameters( method, paramNames, paramTypes, mirror, value, hql ); + checkParameters( method, paramNames, paramTypes, mirror, value, queryString ); } } } @@ -1120,11 +1120,11 @@ else if ( selection instanceof JpaRoot ) { private void validateSql( ExecutableElement method, AnnotationMirror mirror, - String hql, + String sql, List paramNames, AnnotationValue value) { // for SQL queries check that there is a method parameter for every query parameter - ParameterParser.parse(hql, new ParameterRecognizer() { + ParameterParser.parse(sql, new ParameterRecognizer() { int ordinalCount = 0; @Override public void ordinalParameter(int sourcePosition) { From 7e723da507f77a8bdf6233757b0f22b343178c98 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 7 Feb 2024 20:26:47 +0100 Subject: [PATCH 134/321] validate the type arg of Order parameters in @Find and @HQL methods --- .../annotation/AnnotationMetaEntity.java | 73 ++++++++++++++++++- .../jpamodelgen/test/hqlsql/Books.java | 2 +- 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java index 80abe9c981b5..61b9dd4f8acd 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java @@ -26,6 +26,7 @@ import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.WildcardType; import javax.tools.Diagnostic; import org.checkerframework.checker.nullness.qual.Nullable; @@ -41,6 +42,7 @@ import org.hibernate.jpamodelgen.validation.ProcessorSessionFactory; import org.hibernate.jpamodelgen.validation.Validation; import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.query.Order; import org.hibernate.query.criteria.JpaEntityJoin; import org.hibernate.query.criteria.JpaRoot; import org.hibernate.query.criteria.JpaSelection; @@ -592,9 +594,23 @@ private void createCriteriaFinder( final List paramTypes = parameterTypes( method ); final String[] sessionType = sessionTypeFromParameters( paramNames, paramTypes ); final String methodKey = methodName + paramTypes; - for ( VariableElement param : method.getParameters() ) { - if ( isFinderParameterMappingToAttribute( param ) ) { - validateFinderParameter( entity, param ); + for ( VariableElement parameter : method.getParameters() ) { + if ( isFinderParameterMappingToAttribute( parameter ) ) { + validateFinderParameter( entity, parameter ); + } + else { + final TypeMirror parameterType = parameter.asType(); + if ( isOrderParam( parameterType.toString() ) ) { + final TypeMirror typeArgument = getTypeArgument( parameterType ); + if ( typeArgument == null ) { + context.message( parameter, "missing type of order (should be 'Order')", Diagnostic.Kind.ERROR ); + } + else if ( !context.getTypeUtils().isSameType( typeArgument, entity.asType() ) ) { + context.message( parameter, "mismatched type of order (should be 'Order')", Diagnostic.Kind.ERROR); + } + } } } putMember( methodKey, @@ -615,6 +631,38 @@ private void createCriteriaFinder( ); } + private static @Nullable TypeMirror getTypeArgument(TypeMirror parameterType) { + switch ( parameterType.getKind() ) { + case ARRAY: + final ArrayType arrayType = (ArrayType) parameterType; + return getTypeArgument( arrayType.getComponentType() ); + case DECLARED: + final DeclaredType type = (DeclaredType) parameterType; + if ( parameterType.toString().startsWith( List.class.getName() ) ) { + for (TypeMirror arg : type.getTypeArguments()) { + return getTypeArgument( arg ); + } + } + else if ( parameterType.toString().startsWith( Order.class.getName() ) ) { + for ( TypeMirror arg : type.getTypeArguments() ) { + switch ( arg.getKind() ) { + case WILDCARD: + return ((WildcardType) arg).getSuperBound(); + case ARRAY: + case DECLARED: + case TYPEVAR: + return arg; + default: + return null; + } + } + } + return null; + default: + return null; + } + } + private static boolean isFinderParameterMappingToAttribute(VariableElement param) { final String type = param.asType().toString(); return !isSessionParameter( type ) @@ -1006,7 +1054,7 @@ private void addQueryMethod( } // now check that the query has a parameter for every method parameter - checkParameters( method, paramNames, paramTypes, mirror, value, queryString ); + checkParameters( method, returnType, paramNames, paramTypes, mirror, value, queryString ); } } } @@ -1375,6 +1423,7 @@ private static boolean isNullable(Element member) { private void checkParameters( ExecutableElement method, + @Nullable TypeMirror returnType, List paramNames, List paramTypes, AnnotationMirror mirror, AnnotationValue value, @@ -1389,6 +1438,22 @@ private void checkParameters( Diagnostic.Kind.ERROR ); } } + if ( returnType != null ) { + for ( VariableElement parameter : method.getParameters() ) { + final TypeMirror parameterType = parameter.asType(); + final TypeMirror typeArgument = getTypeArgument( parameterType ); + if ( isOrderParam( parameterType.toString() ) ) { + if ( typeArgument == null ) { + context.message( parameter, "missing type of order (should be 'Order')", Diagnostic.Kind.ERROR ); + } + else if ( !context.getTypeUtils().isSameType(typeArgument, returnType) ) { + context.message( parameter, "mismatched type of order (should be 'Order')", Diagnostic.Kind.ERROR ); + } + } + } + } } private static boolean parameterIsMissing(String hql, int i, String param, String type) { diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hqlsql/Books.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hqlsql/Books.java index 1f3c00ed6ce5..de5ee8618b6b 100644 --- a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hqlsql/Books.java +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hqlsql/Books.java @@ -32,5 +32,5 @@ public abstract class Books { static class Summary { Summary(String title, String publisher, String isbn) {} } @HQL("select title, publisher.name, isbn from Book") - abstract List

    summarize(Session session, Order order); + abstract List summarize(Session session, Order order); } From d234b4eea93a699980c33bc7dae42dcf7e08de5e Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 5 Jan 2024 10:32:07 +0100 Subject: [PATCH 135/321] HHH-14821 consistent use of exception types (and exception message formats) --- .../event/internal/DefaultRefreshEventListener.java | 11 ++++------- .../org/hibernate/orm/test/refresh/RefreshTest.java | 8 +++----- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java index b4025f31c2a5..9aa230a4c0d2 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java @@ -9,7 +9,8 @@ import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; -import org.hibernate.PersistentObjectException; +import org.hibernate.NonUniqueObjectException; +import org.hibernate.TransientObjectException; import org.hibernate.UnresolvableObjectException; import org.hibernate.cache.spi.access.CollectionDataAccess; import org.hibernate.cache.spi.access.EntityDataAccess; @@ -128,8 +129,7 @@ private static void refresh(RefreshEvent event, RefreshContext refreshedAlready, persister = source.getEntityPersister( event.getEntityName(), object ); id = persister.getIdentifier( object, event.getSession() ); if ( id == null ) { - throw new HibernateException( "attempted to refresh an instance that is not part of the persistence context yet: " - + infoString( persister, id, source.getFactory() )); + throw new TransientObjectException( "transient instance passed to refresh"); } if ( LOG.isTraceEnabled() ) { LOG.tracev( @@ -138,10 +138,7 @@ private static void refresh(RefreshEvent event, RefreshContext refreshedAlready, ); } if ( persistenceContext.getEntry( source.generateEntityKey( id, persister ) ) != null ) { - throw new PersistentObjectException( - "attempted to refresh transient instance when persistent instance was already associated with the Session: " - + infoString( persister, id, source.getFactory() ) - ); + throw new NonUniqueObjectException( id, persister.getEntityName() ); } } else { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/refresh/RefreshTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/refresh/RefreshTest.java index 0dd05697db15..120d1197c292 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/refresh/RefreshTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/refresh/RefreshTest.java @@ -6,8 +6,7 @@ import java.util.LinkedList; import java.util.Set; -import org.hibernate.HibernateException; - +import org.hibernate.TransientObjectException; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; @@ -81,7 +80,7 @@ public void testIt(SessionFactoryScope scope) { @Test public void testRefreshWithNullId(SessionFactoryScope scope) { Assertions.assertThrows( - HibernateException.class, + TransientObjectException.class, () -> { scope.inTransaction( session -> { @@ -90,8 +89,7 @@ public void testRefreshWithNullId(SessionFactoryScope scope) { session.refresh( se ); } ); - }, - "attempted to refresh an instance that is not part of the persistence context yet: [org.hibernate.orm.test.refresh.RefreshTest$SimpleEntity#]" + } ); } From be3170a197d8490064be0baa95f29db6db0bacaf Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Mon, 12 Feb 2024 14:04:18 +0100 Subject: [PATCH 136/321] Assume "yes" for pgvector package installation in db startup script --- docker_db.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docker_db.sh b/docker_db.sh index 3079de33bc9b..b778b7f3a2d2 100755 --- a/docker_db.sh +++ b/docker_db.sh @@ -194,33 +194,33 @@ postgresql() { postgresql_12() { $CONTAINER_CLI rm -f postgres || true $CONTAINER_CLI run --name postgres -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p5432:5432 -d docker.io/postgis/postgis:12-3.4 - $CONTAINER_CLI exec postgres bash -c '/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && apt install postgresql-12-pgvector && psql -U hibernate_orm_test -d hibernate_orm_test -c "create extension vector;"' + $CONTAINER_CLI exec postgres bash -c '/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && apt install -y postgresql-12-pgvector && psql -U hibernate_orm_test -d hibernate_orm_test -c "create extension vector;"' } postgresql_13() { $CONTAINER_CLI rm -f postgres || true $CONTAINER_CLI run --name postgres -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p5432:5432 -d docker.io/postgis/postgis:13-3.1 - $CONTAINER_CLI exec postgres bash -c '/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && apt install postgresql-13-pgvector && psql -U hibernate_orm_test -d hibernate_orm_test -c "create extension vector;"' + $CONTAINER_CLI exec postgres bash -c '/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && apt install -y postgresql-13-pgvector && psql -U hibernate_orm_test -d hibernate_orm_test -c "create extension vector;"' } postgresql_14() { $CONTAINER_CLI rm -f postgres || true $CONTAINER_CLI run --name postgres -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p5432:5432 -d docker.io/postgis/postgis:14-3.3 - $CONTAINER_CLI exec postgres bash -c '/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && apt install postgresql-14-pgvector && psql -U hibernate_orm_test -d hibernate_orm_test -c "create extension vector;"' + $CONTAINER_CLI exec postgres bash -c '/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && apt install -y postgresql-14-pgvector && psql -U hibernate_orm_test -d hibernate_orm_test -c "create extension vector;"' } postgresql_15() { $CONTAINER_CLI rm -f postgres || true $CONTAINER_CLI run --name postgres -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p5432:5432 --tmpfs /pgtmpfs:size=131072k -d docker.io/postgis/postgis:15-3.3 \ -c fsync=off -c synchronous_commit=off -c full_page_writes=off -c shared_buffers=256MB -c maintenance_work_mem=256MB -c max_wal_size=1GB -c checkpoint_timeout=1d - $CONTAINER_CLI exec postgres bash -c '/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && apt install postgresql-15-pgvector && psql -U hibernate_orm_test -d hibernate_orm_test -c "create extension vector;"' + $CONTAINER_CLI exec postgres bash -c '/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && apt install -y postgresql-15-pgvector && psql -U hibernate_orm_test -d hibernate_orm_test -c "create extension vector;"' } postgresql_16() { $CONTAINER_CLI rm -f postgres || true $CONTAINER_CLI run --name postgres -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p5432:5432 --tmpfs /pgtmpfs:size=131072k -d docker.io/postgis/postgis:16-3.4 \ -c fsync=off -c synchronous_commit=off -c full_page_writes=off -c shared_buffers=256MB -c maintenance_work_mem=256MB -c max_wal_size=1GB -c checkpoint_timeout=1d - $CONTAINER_CLI exec postgres bash -c '/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && apt install postgresql-16-pgvector && psql -U hibernate_orm_test -d hibernate_orm_test -c "create extension vector;"' + $CONTAINER_CLI exec postgres bash -c '/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && apt install -y postgresql-16-pgvector && psql -U hibernate_orm_test -d hibernate_orm_test -c "create extension vector;"' } edb() { From d865ec9e36f36f897f4248cf96228558ca341af8 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 7 Feb 2024 10:39:56 +0100 Subject: [PATCH 137/321] HHH-17695 Add test for issue --- .../embeddable/NestedJsonEmbeddableTest.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java index 90e9b34ceafb..fb46cb55b81c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java @@ -28,6 +28,7 @@ import org.hibernate.testing.orm.domain.gambit.MutableValue; import org.hibernate.testing.orm.junit.BaseSessionFactoryFunctionalTest; import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -230,6 +231,68 @@ public void testUpdateAggregate() { ); } + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-17695" ) + public void testNullNestedAggregate() { + sessionFactoryScope().inTransaction( + entityManager -> { + JsonHolder jsonHolder = new JsonHolder(3L, "abc", 30, "String 'xyz'", null ); + entityManager.persist( jsonHolder ); + } + ); + sessionFactoryScope().inTransaction( + entityManager -> { + JsonHolder jsonHolder = entityManager.createQuery( "from JsonHolder b where b.id = 3", JsonHolder.class ).getSingleResult(); + assertEquals( "abc", jsonHolder.theJson.stringField ); + assertEquals( 30, jsonHolder.theJson.simpleEmbeddable.integerField ); + assertEquals( "String 'xyz'", jsonHolder.theJson.simpleEmbeddable.doubleNested.theNested.theLeaf.stringField ); + assertNull( jsonHolder.getAggregate() ); + } + ); + } + + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-17695" ) + public void testNullNestedEmbeddable() { + sessionFactoryScope().inTransaction( + entityManager -> { + JsonHolder jsonHolder = new JsonHolder( ); + jsonHolder.setId( 3L ); + jsonHolder.setTheJson( new TheJson( "abc", null, EmbeddableAggregate.createAggregate1() ) ); + entityManager.persist( jsonHolder ); + } + ); + sessionFactoryScope().inTransaction( + entityManager -> { + JsonHolder jsonHolder = entityManager.createQuery( "from JsonHolder b where b.id = 3", JsonHolder.class ).getSingleResult(); + assertEquals( "abc", jsonHolder.theJson.stringField ); + assertNull( jsonHolder.theJson.simpleEmbeddable ); + assertStructEquals( EmbeddableAggregate.createAggregate1(), jsonHolder.getAggregate() ); + } + ); + } + + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-17695" ) + public void testNullNestedEmbeddableAndAggregate() { + sessionFactoryScope().inTransaction( + entityManager -> { + JsonHolder jsonHolder = new JsonHolder( ); + jsonHolder.setId( 3L ); + jsonHolder.setTheJson( new TheJson( "abc", null, null ) ); + entityManager.persist( jsonHolder ); + } + ); + sessionFactoryScope().inTransaction( + entityManager -> { + JsonHolder jsonHolder = entityManager.createQuery( "from JsonHolder b where b.id = 3", JsonHolder.class ).getSingleResult(); + assertEquals( "abc", jsonHolder.theJson.stringField ); + assertNull( jsonHolder.theJson.simpleEmbeddable ); + assertNull( jsonHolder.getAggregate() ); + } + ); + } + @Test @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonComponentUpdate.class) public void testUpdateAggregateMember() { @@ -393,6 +456,12 @@ public TheJson(String stringField, Integer integerField, String leaf, Embeddable this.simpleEmbeddable = new SimpleEmbeddable( integerField, leaf ); this.nested = nested; } + + public TheJson(String stringField, SimpleEmbeddable simpleEmbeddable, EmbeddableAggregate nested) { + this.stringField = stringField; + this.simpleEmbeddable = simpleEmbeddable; + this.nested = nested; + } } @Embeddable From 46dffe3336c1b28b49b63ffcdaf4b9a42aed934d Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 7 Feb 2024 10:40:06 +0100 Subject: [PATCH 138/321] HHH-17695 Skip rendering null nested JSON aggregate embeddables --- .../src/main/java/org/hibernate/dialect/JsonHelper.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/dialect/JsonHelper.java index fa6712b55f0f..701bc399c079 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/JsonHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/JsonHelper.java @@ -70,13 +70,13 @@ private static void toString( toString( attributeMapping.getMappedType(), values[i], options, appender ); } else if ( attributeMapping instanceof EmbeddedAttributeMapping ) { + if ( values[i] == null ) { + // Skipping the update of the separator is on purpose + continue; + } final EmbeddableMappingType mappingType = (EmbeddableMappingType) attributeMapping.getMappedType(); final SelectableMapping aggregateMapping = mappingType.getAggregateMapping(); if ( aggregateMapping == null ) { - if ( values[i] == null ) { - // Skipping the update of the separator is on purpose - continue; - } toString( mappingType, options, From 6b56b7e7373d2297d06f4e38b874ba2632aec109 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 13 Feb 2024 12:16:57 +0100 Subject: [PATCH 139/321] HHH-17701 Add test for issue --- ...InSubqueryPredicateAnonymousTupleTest.java | 75 ++++++++++++++++++- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/InSubqueryPredicateAnonymousTupleTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/InSubqueryPredicateAnonymousTupleTest.java index d69b83b2f09f..f86a98ff49b2 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/InSubqueryPredicateAnonymousTupleTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/InSubqueryPredicateAnonymousTupleTest.java @@ -15,18 +15,31 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Convert; +import jakarta.persistence.Converter; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + import static org.assertj.core.api.Assertions.assertThat; /** * @author Marco Belladelli */ -@DomainModel( annotatedClasses = BasicEntity.class ) +@DomainModel( annotatedClasses = { + BasicEntity.class, + InSubqueryPredicateAnonymousTupleTest.TestEntity.class, +} ) @SessionFactory @Jira( "https://hibernate.atlassian.net/browse/HHH-17332" ) +@Jira( "https://hibernate.atlassian.net/browse/HHH-17701" ) public class InSubqueryPredicateAnonymousTupleTest { @BeforeAll public void setUp(SessionFactoryScope scope) { - scope.inTransaction( session -> session.persist( new BasicEntity( 1, "test" ) ) ); + scope.inTransaction( session -> { + session.persist( new BasicEntity( 1, "test" ) ); + session.persist( new TestEntity( 1L, new Money( 100L ) ) ); + } ); } @AfterAll @@ -58,4 +71,62 @@ public void testTupleInSubqueryPredicate(SessionFactoryScope scope) { assertThat( result ).isEqualTo( "test" ); } ); } + + @Test + public void testConvertedAttributeTuple(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final TestEntity result = session.createQuery( + "select t from TestEntity t where (t.id, t.money) in " + + "(select t2.id, t2.money from TestEntity t2)", + TestEntity.class + ).getSingleResult(); + assertThat( result.getMoney().getCents() ).isEqualTo( 100L ); + } ); + } + + @Entity( name = "TestEntity" ) + public static class TestEntity { + @Id + private Long id; + + @Convert( converter = MoneyConverter.class ) + private Money money; + + public TestEntity() { + } + + public TestEntity(Long id, Money money) { + this.id = id; + this.money = money; + } + + public Money getMoney() { + return money; + } + } + + public static class Money { + private long cents; + + public Money(long cents) { + this.cents = cents; + } + + public long getCents() { + return cents; + } + } + + @Converter + public static class MoneyConverter implements AttributeConverter { + @Override + public Long convertToDatabaseColumn(Money attribute) { + return attribute == null ? null : attribute.getCents(); + } + + @Override + public Money convertToEntityAttribute(Long dbData) { + return dbData == null ? null : new Money( dbData ); + } + } } From 08db8487b0aea0c9808861e025c0840cf9f11c41 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Mon, 5 Feb 2024 12:20:13 +0100 Subject: [PATCH 140/321] HHH-17701 Use relational java type when resolving mapping expressibles --- .../model/domain/internal/MappingMetamodelImpl.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java index 2988762c9cb4..a96a2ef7a1f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java @@ -775,12 +775,11 @@ public MappingModelExpressible resolveMappingExpressible( } if ( sqmExpressible instanceof BasicDomainType ) { - final BasicDomainType domainType = (BasicDomainType) sqmExpressible; - return getTypeConfiguration().getBasicTypeForJavaType( domainType.getExpressibleJavaType().getJavaTypeClass() ); + return getTypeConfiguration().getBasicTypeForJavaType( sqmExpressible.getRelationalJavaType().getJavaType() ); } if ( sqmExpressible instanceof BasicSqmPathSource ) { - return getTypeConfiguration().getBasicTypeForJavaType(((BasicSqmPathSource) sqmExpressible).getJavaType()); + return getTypeConfiguration().getBasicTypeForJavaType( sqmExpressible.getRelationalJavaType().getJavaType() ); } if ( sqmExpressible instanceof SqmFieldLiteral ) { From 584518d6098728c753e499b9f72d66f546ed0df6 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Fri, 9 Feb 2024 12:53:17 +0100 Subject: [PATCH 141/321] HHH-17508 Add test for issue --- .../query/criteria/CriteriaCteUnionTest.java | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaCteUnionTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaCteUnionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaCteUnionTest.java new file mode 100644 index 000000000000..1daa101c489e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaCteUnionTest.java @@ -0,0 +1,129 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.criteria; + +import java.util.List; +import java.util.function.Consumer; + +import org.hibernate.query.criteria.HibernateCriteriaBuilder; +import org.hibernate.query.criteria.JpaCriteriaQuery; +import org.hibernate.query.criteria.JpaCteCriteria; +import org.hibernate.query.criteria.JpaRoot; + +import org.hibernate.testing.orm.domain.gambit.BasicEntity; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Tuple; +import jakarta.persistence.criteria.CriteriaQuery; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Marco Belladelli + */ +@DomainModel( annotatedClasses = BasicEntity.class ) +@SessionFactory +@Jira( "https://hibernate.atlassian.net/browse/HHH-17508" ) +public class CriteriaCteUnionTest { + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.persist( new BasicEntity( 1, "entity_1" ) ); + session.persist( new BasicEntity( 2, "entity_2" ) ); + } ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createMutationQuery( "delete from BasicEntity" ).executeUpdate() ); + } + + @Test + public void testSimpleCte(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); + final JpaCriteriaQuery query = createQuery( cb, true, "entity_1" ); + final JpaCriteriaQuery mainQuery = cb.createTupleQuery(); + + final JpaCteCriteria cteQuery = mainQuery.with( query ); + final JpaRoot mainRoot = mainQuery.from( cteQuery ); + mainQuery.multiselect( + mainRoot.get( "id" ).alias( "id" ), + mainRoot.get( "isMatch" ).alias( "isMatch" ) + ); + final Tuple criteriaResult = session.createQuery( mainQuery ).getSingleResult(); + + final Tuple hqlResult = session.createQuery( + "with cte as (" + createHql( true, "entity_1" ) + ")" + + "select id, isMatch from cte", + Tuple.class + ).getSingleResult(); + + assertThat( hqlResult.get( 0, Integer.class ) ) + .isEqualTo( criteriaResult.get( 0, Integer.class ) ) + .isEqualTo( 1 ); + assertThat( hqlResult.get( 1, Boolean.class ) ) + .isEqualTo( criteriaResult.get( 1, Boolean.class ) ) + .isTrue(); + } ); + } + + @Test + public void testUnionCte(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); + + final JpaCriteriaQuery query1 = createQuery( cb, true, "entity_1" ); + final JpaCriteriaQuery query2 = createQuery( cb, false, "entity_2" ); + final CriteriaQuery unionQuery = cb.unionAll( query1, query2 ); + + final JpaCriteriaQuery mainQuery = cb.createTupleQuery(); + final JpaCteCriteria cteQuery = mainQuery.with( unionQuery ); + final JpaRoot mainRoot = mainQuery.from( cteQuery ); + mainQuery.multiselect( + mainRoot.get( "id" ).alias( "id" ), + mainRoot.get( "isMatch" ).alias( "isMatch" ) + ); + final List criteriaList = session.createQuery( mainQuery ).getResultList(); + + final List hqlList = session.createQuery( + "with cte as (" + + createHql( true, "entity_1" ) + + " union all " + + createHql( false, "entity_2" ) + ") " + + "select id, isMatch from cte", + Tuple.class + ).getResultList(); + + assertThat( hqlList.size() ).isEqualTo( criteriaList.size() ).isEqualTo( 2 ); + final Consumer assertResult = t -> assertThat( t.get( 1, Boolean.class ) ).isEqualTo( t.get( 0, Integer.class ) == 1 ); + hqlList.forEach( assertResult ); + criteriaList.forEach( assertResult ); + } ); + } + + private String createHql(boolean match, String value) { + return String.format( "select id as id, %s as isMatch from BasicEntity e where e.data = '%s'", match, value ); + } + + private JpaCriteriaQuery createQuery(HibernateCriteriaBuilder cb, boolean match, String value) { + final JpaCriteriaQuery cq = cb.createTupleQuery(); + final JpaRoot root = cq.from( BasicEntity.class ); + return cq.multiselect( + root.get( "id" ).alias( "id" ), + cb.literal( match ).alias( "isMatch" ) + ).where( + cb.equal( root.get( "data" ), value ) + ); + } +} From 2e9e472bd734d960c4c1fdf918bf88831e13e140 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Fri, 9 Feb 2024 13:08:47 +0100 Subject: [PATCH 142/321] HHH-17508 Fix using union queries as CTE in criteria --- .../java/org/hibernate/query/sqm/tree/cte/SqmCteTable.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTable.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTable.java index 246aa603ea66..cbaafd48b326 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTable.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTable.java @@ -57,7 +57,8 @@ public static SqmCteTable createStatementTable( String name, SqmCteStatement cteStatement, SqmSelectQuery selectStatement) { - final SqmSelectableNode[] sqmSelectableNodes = selectStatement.getQuerySpec() + final SqmSelectableNode[] sqmSelectableNodes = selectStatement.getQueryPart() + .getFirstQuerySpec() .getSelectClause() .getSelectionItems() .toArray( SqmSelectableNode[]::new ); From c6c96713850343192eaa81782eaa331327553aca Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 14 Feb 2024 09:46:20 +0100 Subject: [PATCH 143/321] HHH-17096 Add test for issue --- .../inheritance/ManyToManyTreatJoinTest.java | 299 ++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/ManyToManyTreatJoinTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/ManyToManyTreatJoinTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/ManyToManyTreatJoinTest.java new file mode 100644 index 000000000000..5e83f66318b1 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/ManyToManyTreatJoinTest.java @@ -0,0 +1,299 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.orm.test.inheritance; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.hibernate.testing.jdbc.SQLStatementInspector; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.ManyToMany; + +import static org.assertj.core.api.Assertions.assertThat; + +@DomainModel( annotatedClasses = { + ManyToManyTreatJoinTest.ParentEntity.class, + ManyToManyTreatJoinTest.SingleBase.class, + ManyToManyTreatJoinTest.SingleSub1.class, + ManyToManyTreatJoinTest.SingleSub2.class, + ManyToManyTreatJoinTest.JoinedBase.class, + ManyToManyTreatJoinTest.JoinedSub1.class, + ManyToManyTreatJoinTest.JoinedSub2.class, + ManyToManyTreatJoinTest.UnionBase.class, + ManyToManyTreatJoinTest.UnionSub1.class, + ManyToManyTreatJoinTest.UnionSub2.class, +} ) +@SessionFactory( useCollectingStatementInspector = true ) +public class ManyToManyTreatJoinTest { + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final ParentEntity parent1 = new ParentEntity( 1 ); + parent1.getSingleEntities().add( new SingleSub1( 2, 2 ) ); + parent1.getJoinedEntities().add( new JoinedSub1( 3, 3 ) ); + parent1.getUnionEntities().put( 4, new UnionSub1( 4, 4 ) ); + session.persist( parent1 ); + final ParentEntity parent2 = new ParentEntity( 5 ); + parent2.getSingleEntities().add( new SingleSub2( 6 ) ); + parent2.getJoinedEntities().add( new JoinedSub2( 7 ) ); + parent2.getUnionEntities().put( 8, new UnionSub2( 8 ) ); + session.persist( parent2 ); + } ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createQuery( "from ParentEntity", ParentEntity.class ) + .getResultList() + .forEach( session::remove ) ); + } + + @Test + public void testSingleTableSelectChild(SessionFactoryScope scope) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + inspector.clear(); + scope.inTransaction( session -> { + final Integer result = session.createSelectionQuery( + "select s.subProp from ParentEntity p join treat(p.singleEntities as SingleSub1) s", + Integer.class + ).getSingleResult(); + assertThat( result ).isEqualTo( 2 ); + inspector.assertNumberOfJoins( 0, 2 ); + } ); + } + + @Test + public void testSingleTableSelectParent(SessionFactoryScope scope) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + inspector.clear(); + scope.inTransaction( session -> { + final Integer result = session.createSelectionQuery( + "select p.id from ParentEntity p join treat(p.singleEntities as SingleSub1) s", + Integer.class + ).getSingleResult(); + assertThat( result ).isEqualTo( 1 ); + // We always join the element table to restrict to the correct subtype + inspector.assertNumberOfJoins( 0, 2 ); + } ); + } + + @Test + public void testJoinedSelectChild(SessionFactoryScope scope) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + inspector.clear(); + scope.inTransaction( session -> { + final Integer result = session.createSelectionQuery( + "select j.subProp from ParentEntity p join treat(p.joinedEntities as JoinedSub1) j", + Integer.class + ).getSingleResult(); + assertThat( result ).isEqualTo( 3 ); + inspector.assertNumberOfJoins( 0, 3 ); + } ); + } + + @Test + public void testJoinedSelectParent(SessionFactoryScope scope) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + inspector.clear(); + scope.inTransaction( session -> { + final Integer result = session.createSelectionQuery( + "select p.id from ParentEntity p join treat(p.joinedEntities as JoinedSub1) j", + Integer.class + ).getSingleResult(); + assertThat( result ).isEqualTo( 1 ); + inspector.assertNumberOfJoins( 0, 3 ); + } ); + } + + @Test + public void testTablePerClassSelectChild(SessionFactoryScope scope) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + inspector.clear(); + scope.inTransaction( session -> { + final Integer result = session.createSelectionQuery( + "select u.subProp from ParentEntity p join treat(p.unionEntities as UnionSub1) u", + Integer.class + ).getSingleResult(); + assertThat( result ).isEqualTo( 4 ); + inspector.assertNumberOfJoins( 0, 2 ); + } ); + } + + @Test + public void testTablePerClassSelectParent(SessionFactoryScope scope) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + inspector.clear(); + scope.inTransaction( session -> { + final Integer result = session.createSelectionQuery( + "select p.id from ParentEntity p join treat(p.unionEntities as UnionSub1) u", + Integer.class + ).getSingleResult(); + assertThat( result ).isEqualTo( 1 ); + inspector.assertNumberOfJoins( 0, 2 ); + } ); + } + + @Entity( name = "ParentEntity" ) + public static class ParentEntity { + @Id + private Integer id; + + @ManyToMany( cascade = CascadeType.PERSIST ) + private List singleEntities = new ArrayList<>(); + + @ManyToMany( cascade = CascadeType.PERSIST ) + private Set joinedEntities = new HashSet<>(); + + @ManyToMany( cascade = CascadeType.PERSIST ) + private Map unionEntities = new HashMap<>(); + + public ParentEntity() { + } + + public ParentEntity(Integer id) { + this.id = id; + } + + public List getSingleEntities() { + return singleEntities; + } + + public Set getJoinedEntities() { + return joinedEntities; + } + + public Map getUnionEntities() { + return unionEntities; + } + } + + @Entity( name = "SingleBase" ) + @Inheritance( strategy = InheritanceType.SINGLE_TABLE ) + public static abstract class SingleBase { + @Id + private Integer id; + + public SingleBase() { + } + + public SingleBase(Integer id) { + this.id = id; + } + } + + @Entity( name = "SingleSub1" ) + public static class SingleSub1 extends SingleBase { + private Integer subProp; + + public SingleSub1() { + } + + public SingleSub1(Integer id, Integer subProp) { + super( id ); + this.subProp = subProp; + } + } + + @Entity( name = "SingleSub2" ) + public static class SingleSub2 extends SingleBase { + public SingleSub2() { + } + + public SingleSub2(Integer id) { + super( id ); + } + } + + @Entity( name = "JoinedBase" ) + @Inheritance( strategy = InheritanceType.JOINED ) + public static abstract class JoinedBase { + @Id + private Integer id; + + public JoinedBase() { + } + + public JoinedBase(Integer id) { + this.id = id; + } + } + + @Entity( name = "JoinedSub1" ) + public static class JoinedSub1 extends JoinedBase { + private Integer subProp; + + public JoinedSub1() { + } + + public JoinedSub1(Integer id, Integer subProp) { + super( id ); + this.subProp = subProp; + } + } + + @Entity( name = "JoinedSub2" ) + public static class JoinedSub2 extends JoinedBase { + public JoinedSub2() { + } + + public JoinedSub2(Integer id) { + super( id ); + } + } + + @Entity( name = "UnionBase" ) + @Inheritance( strategy = InheritanceType.TABLE_PER_CLASS ) + public static abstract class UnionBase { + @Id + private Integer id; + + public UnionBase() { + } + + public UnionBase(Integer id) { + this.id = id; + } + } + + @Entity( name = "UnionSub1" ) + public static class UnionSub1 extends UnionBase { + private Integer subProp; + + public UnionSub1() { + } + + public UnionSub1(Integer id, Integer subProp) { + super( id ); + this.subProp = subProp; + } + } + + @Entity( name = "UnionSub2" ) + public static class UnionSub2 extends UnionBase { + public UnionSub2() { + } + + public UnionSub2(Integer id) { + super( id ); + } + } +} From a4cbe2f95aba6f6578cb9d70a90c8f00ffd9a9ce Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 14 Feb 2024 09:46:57 +0100 Subject: [PATCH 144/321] HHH-17096 Always initialize table group for subtype treated joins --- .../hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 6e4dbeb676f6..6cb34fb9c212 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -3084,8 +3084,11 @@ private void registerEntityNameUsage( (s, existingUse) -> finalEntityNameUse.stronger( existingUse ) ); - // Resolve the table reference for all types which we register an entity name use for - if ( actualTableGroup.isInitialized() ) { + // Resolve the table reference for all types which we register an entity name use for. + // Also, force table group initialization for treats when needed to ensure correct cardinality + final EntityNameUse.UseKind useKind = finalEntityNameUse.getKind(); + if ( actualTableGroup.isInitialized() || ( useKind == EntityNameUse.UseKind.TREAT && actualTableGroup.canUseInnerJoins() + && !( (EntityMappingType) actualTableGroup.getModelPart().getPartMappingType() ).isTypeOrSuperType( persister ) ) ) { actualTableGroup.resolveTableReference( null, persister.getTableName() ); } @@ -3102,7 +3105,6 @@ private void registerEntityNameUsage( // If we encounter a treat or projection use, we also want register the use for all subtypes. // We do this here to not have to expand entity name uses during pruning later on - final EntityNameUse.UseKind useKind = finalEntityNameUse.getKind(); if ( useKind == EntityNameUse.UseKind.TREAT ) { for ( EntityMappingType subType : persister.getSubMappingTypes() ) { entityNameUses.compute( From 9fc1ba259f067705b7febaba89767d624590a614 Mon Sep 17 00:00:00 2001 From: Ken Schosinsky Date: Fri, 16 Feb 2024 13:07:57 +0100 Subject: [PATCH 145/321] HHH-17742 Test for race condition in ConcreteSqmSelectQueryPlan Race condition occurs when two or more concurrent reach the synchronized block in ConcreteSqmSelectQueryPlan#withCacheableSqmInterpretation. The latter ones will see the cacheableSqmInterpretation by the first one, but don't check whether it is compatible (jdbcSelect.dependsOnParameterBindings(), jdbcSelect.isCompatibleWith). On MySQL this can cause "limit null,1" to be rendered if the first query has both offset and limit, the latter ones only a limit. --- ...urrentConcreteSqmSelectQueryPlainTest.java | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/ConcurrentConcreteSqmSelectQueryPlainTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/ConcurrentConcreteSqmSelectQueryPlainTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/ConcurrentConcreteSqmSelectQueryPlainTest.java new file mode 100644 index 000000000000..efdd342ef3d1 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/ConcurrentConcreteSqmSelectQueryPlainTest.java @@ -0,0 +1,150 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.sqm; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +import org.hibernate.Session; +import org.hibernate.boot.registry.BootstrapServiceRegistry; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; +import org.hibernate.dialect.MySQLDialect; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.query.Query; +import org.hibernate.query.spi.QueryOptions; +import org.hibernate.query.spi.QueryParameterBindings; +import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.sql.SqmTranslator; +import org.hibernate.query.sqm.sql.StandardSqmTranslatorFactory; +import org.hibernate.query.sqm.tree.select.SqmSelectStatement; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; +import org.hibernate.sql.ast.tree.select.SelectStatement; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.orm.junit.RequiresDialect; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +/** + * (Flaky) test for {@link ConcreteSqmSelectQueryPlan#withCacheableSqmInterpretation} not checking for {@link JdbcOperationQuerySelect#dependsOnParameterBindings()}/{@link JdbcOperationQuerySelect#isCompatibleWith(org.hibernate.sql.exec.spi.JdbcParameterBindings, org.hibernate.query.spi.QueryOptions)} in double-lock checking. + * + *

    Might cause incorrect SQL to be rendered. In case my MySQL this might cause "limit null,1" statements. + * + * @see https://hibernate.atlassian.net/browse/HHH-17742 + */ +@RequiresDialect(MySQLDialect.class) +public class ConcurrentConcreteSqmSelectQueryPlainTest extends BaseCoreFunctionalTestCase { + + public static final String QUERY_STRING = "select e from simple e"; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { SimpleEntity.class }; + } + + /** + * First query will generated a "limit ?,?" SQL statement, the following ones only need "limit ?". + * Due to the race condition, the following ones reuse the cached "limit ?,?" statement, resulting in "limit null,?" being generated. + */ + @Test + public void run() throws InterruptedException { + inTransaction( session -> { + for ( int i = 0; i < 2; i++ ) { + SimpleEntity entity = new SimpleEntity(); + entity.setId( i ); + session.persist( entity ); + } + } ); + + CompletableFuture>[] results = new CompletableFuture[5]; + ExecutorService executorService = Executors.newFixedThreadPool( results.length ); + + for ( int i = 0; i < results.length; i++ ) { + int index = i; + results[i] = CompletableFuture.supplyAsync( () -> executeQuery( index ), executorService ); + } + for ( int i = 0; i < results.length; i++ ) { + assertThat( results[i].join() ).hasSize( 1 ); + } + + executorService.shutdown(); + } + + private List executeQuery(int index) { + try (Session session = sessionFactory().openSession()) { + return executeQuery( session, index ); + } + } + + private List executeQuery(Session session, int index) { + Query query = session.createQuery( QUERY_STRING, SimpleEntity.class ) + .setMaxResults( 1 ); + + if ( index == 0 ) { + query.setFirstResult( 1 ); + } else { + try { + Thread.sleep( 500L ); // sleep to "ensure" all queries use the same SelectQueryPlan instance (QuerySqmImpl#resolveSelectQueryPlan) + } + catch (InterruptedException ex) { + fail( "sleep interrupted: query " + index, ex ); + } + } + + return query.list(); + } + + @Override + protected Configuration constructAndConfigureConfiguration(BootstrapServiceRegistry bootstrapServiceRegistry) { + Configuration cfg = super.constructAndConfigureConfiguration( bootstrapServiceRegistry ); + cfg.setProperty( AvailableSettings.SEMANTIC_QUERY_TRANSLATOR, DelayingStandardSqmTranslatorFactory.class.getName() ); + + return cfg; + } + + @Entity(name = "simple") + public static class SimpleEntity { + + @Id + private Integer id; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + } + + public static class DelayingStandardSqmTranslatorFactory extends StandardSqmTranslatorFactory { + + @Override + public SqmTranslator createSelectTranslator(SqmSelectStatement sqmSelectStatement, QueryOptions queryOptions, + DomainParameterXref domainParameterXref, QueryParameterBindings domainParameterBindings, LoadQueryInfluencers loadQueryInfluencers, + SqlAstCreationContext creationContext, boolean deduplicateSelectionItems) { + + try { + Thread.sleep( 2000L ); // delay to trigger double-lock checking by concurrent queries + } + catch (InterruptedException ex) { + fail( "sleep interrupted: createSelectTranslator", ex ); + } + + return super.createSelectTranslator( sqmSelectStatement, queryOptions, domainParameterXref, domainParameterBindings, loadQueryInfluencers, creationContext, + deduplicateSelectionItems ); + } + + } + +} From fff89542a6a2b12d04bf89017c81da35ede217af Mon Sep 17 00:00:00 2001 From: Ken Schosinsky Date: Fri, 16 Feb 2024 15:08:17 +0100 Subject: [PATCH 146/321] HHH-17742 Fix race condition in ConcreteSqmSelectQueryPlan --- .../internal/ConcreteSqmSelectQueryPlan.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java index 70b11fe518e9..fc252ccb5ef3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java @@ -333,6 +333,25 @@ private T withCacheableSqmInterpretation(DomainQueryExecutionContext exec localCopy.firstParameterBindings = null; cacheableSqmInterpretation = localCopy; } + else { + // If the translation depends on parameter bindings or it isn't compatible with the current query options, + // we have to rebuild the JdbcSelect, which is still better than having to translate from SQM to SQL AST again + if ( localCopy.jdbcSelect.dependsOnParameterBindings() ) { + jdbcParameterBindings = createJdbcParameterBindings( localCopy, executionContext ); + } + // If the translation depends on the limit or lock options, we have to rebuild the JdbcSelect + // We could avoid this by putting the lock options into the cache key + if ( !localCopy.jdbcSelect.isCompatibleWith( jdbcParameterBindings, executionContext.getQueryOptions() ) ) { + localCopy = buildCacheableSqmInterpretation( + sqm, + domainParameterXref, + executionContext + ); + jdbcParameterBindings = localCopy.firstParameterBindings; + localCopy.firstParameterBindings = null; + cacheableSqmInterpretation = localCopy; + } + } } } else { From c44823a326a8e1a0c4b4a3063d2e6a4dab2f0a2f Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Mon, 19 Feb 2024 14:37:42 +0100 Subject: [PATCH 147/321] Make sure test.jdk.launcher.args is passed as jvm args to test launcher --- .../orm/toolchains/JavaModulePlugin.java | 5 ++++ .../orm/toolchains/JdkVersionConfig.java | 24 ++++++++++++++----- .../orm/toolchains/JdkVersionPlugin.java | 6 ++++- .../toolchains/JdkVersionSettingsPlugin.java | 8 ++++++- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JavaModulePlugin.java b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JavaModulePlugin.java index e197951b8dc1..6be372d5b7bc 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JavaModulePlugin.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JavaModulePlugin.java @@ -81,6 +81,11 @@ public void apply(Project project) { javaToolchainSpec.getLanguageVersion().set( jdkVersionsConfig.getTestLauncherVersion() ); } ) ); + + final String launcherArgs = jdkVersionsConfig.getTest().getLauncherArgs(); + if ( launcherArgs != null ) { + testTask.jvmArgs( (Object[]) launcherArgs.split( " " ) ); + } } } } diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionConfig.java b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionConfig.java index 18020922f868..eb7a9582cec1 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionConfig.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionConfig.java @@ -30,6 +30,7 @@ public class JdkVersionConfig { public static final String DSL_NAME = "jdkVersions"; public static final String MAIN_JDK_VERSION = "main.jdk.version"; public static final String TEST_JDK_VERSION = "test.jdk.version"; + public static final String TEST_JDK_LAUNCHER_ARGS = "test.jdk.launcher.args"; private final boolean explicit; private final JavaLanguageVersion baseline; @@ -43,11 +44,12 @@ public JdkVersionConfig( JavaLanguageVersion mainReleaseVersion, JavaLanguageVersion testCompileVersion, JavaLanguageVersion testReleaseVersion, - JavaLanguageVersion testLauncherVersion) { + JavaLanguageVersion testLauncherVersion, + String testLauncherArgs) { this.explicit = explicit; this.baseline = baseline; this.main = new MainJdks( mainCompileVersion, mainReleaseVersion ); - this.test = new TestJdks( testCompileVersion, testReleaseVersion, testLauncherVersion ); + this.test = new TestJdks( testCompileVersion, testReleaseVersion, testLauncherVersion, testLauncherArgs ); } public boolean isExplicitlyConfigured() { @@ -110,7 +112,8 @@ public static JdkVersionConfig createVersionConfig( JavaLanguageVersion explicitTestVersion, JavaLanguageVersion gradleJdkVersion, JavaLanguageVersion baselineJdkVersion, - JavaLanguageVersion maxSupportedJdkVersion) { + JavaLanguageVersion maxSupportedJdkVersion, + String testLauncherArgs) { final boolean explicitlyConfigured = explicitMainVersion != null || explicitTestVersion != null; final JavaLanguageVersion mainCompileVersion; @@ -144,7 +147,8 @@ public static JdkVersionConfig createVersionConfig( mainReleaseVersion, testCompileVersion, testReleaseVersion, - testLauncherVersion + testLauncherVersion, + testLauncherArgs ); } else { @@ -169,7 +173,8 @@ public static JdkVersionConfig createVersionConfig( baselineJdkVersion, gradleJdkVersion, baselineJdkVersion, - gradleJdkVersion + gradleJdkVersion, + testLauncherArgs ); } } @@ -245,14 +250,17 @@ public static class TestJdks implements JdkVersionCombo { private final JavaLanguageVersion compileVersion; private final JavaLanguageVersion releaseVersion; private final JavaLanguageVersion launcherVersion; + private final String launcherArgs; public TestJdks( JavaLanguageVersion compileVersion, JavaLanguageVersion releaseVersion, - JavaLanguageVersion launcherVersion) { + JavaLanguageVersion launcherVersion, + String launcherArgs) { this.compileVersion = compileVersion; this.releaseVersion = releaseVersion; this.launcherVersion = launcherVersion; + this.launcherArgs = launcherArgs; } public JavaLanguageVersion getCompiler() { @@ -273,6 +281,10 @@ public JavaLanguageVersion getLauncher() { return launcherVersion; } + public String getLauncherArgs() { + return launcherArgs; + } + @Override public String toString() { return "[compile: " + compileVersion + ", release:" + releaseVersion + ", launcher: " + launcherVersion + "]"; diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionPlugin.java b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionPlugin.java index e9ad31604847..ee79cfe6eb5b 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionPlugin.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionPlugin.java @@ -26,6 +26,7 @@ import org.jetbrains.annotations.NotNull; import static org.hibernate.orm.toolchains.JdkVersionConfig.MAIN_JDK_VERSION; +import static org.hibernate.orm.toolchains.JdkVersionConfig.TEST_JDK_LAUNCHER_ARGS; import static org.hibernate.orm.toolchains.JdkVersionConfig.TEST_JDK_VERSION; import static org.hibernate.orm.toolchains.JdkVersionConfig.createVersionConfig; import static org.hibernate.orm.toolchains.JdkVersionConfig.extractVersion; @@ -51,13 +52,16 @@ public void apply(Project project) { final VersionCatalog jdkVersions = versionCatalogs.named( "jdks" ); final JavaLanguageVersion baselineJdkVersion = getJavaLanguageVersion( jdkVersions, "baseline" ); final JavaLanguageVersion maxSupportedJdkVersion = getJavaLanguageVersion( jdkVersions, "maxSupportedBytecode" ); + final Object testLauncherArgsObject = project.getProperties().get( TEST_JDK_LAUNCHER_ARGS ); + final String testLauncherArgs = testLauncherArgsObject == null ? null : testLauncherArgsObject.toString(); final JdkVersionConfig jdkVersionConfig = createVersionConfig( explicitMainVersion, explicitTestVersion, gradleJdkVersion, baselineJdkVersion, - maxSupportedJdkVersion + maxSupportedJdkVersion, + testLauncherArgs ); project.getExtensions().add( JdkVersionConfig.DSL_NAME, jdkVersionConfig ); diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionSettingsPlugin.java b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionSettingsPlugin.java index 66b3f19dbd1f..245f19ead5e3 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionSettingsPlugin.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionSettingsPlugin.java @@ -6,6 +6,7 @@ */ package org.hibernate.orm.toolchains; +import org.gradle.StartParameter; import org.gradle.api.JavaVersion; import org.gradle.api.Plugin; import org.gradle.api.artifacts.VersionCatalog; @@ -17,6 +18,7 @@ import org.jetbrains.annotations.NotNull; import static org.hibernate.orm.toolchains.JdkVersionConfig.MAIN_JDK_VERSION; +import static org.hibernate.orm.toolchains.JdkVersionConfig.TEST_JDK_LAUNCHER_ARGS; import static org.hibernate.orm.toolchains.JdkVersionConfig.TEST_JDK_VERSION; import static org.hibernate.orm.toolchains.JdkVersionConfig.createVersionConfig; import static org.hibernate.orm.toolchains.JdkVersionConfig.extractVersion; @@ -39,13 +41,17 @@ public void apply(Settings settings) { // maxSupportedJdkVersion = getJavaLanguageVersion( jdkVersions, "maxSupportedBytecode" ); baselineJdkVersion = JavaLanguageVersion.of( "11" ); maxSupportedJdkVersion = JavaLanguageVersion.of( "17" ); + final StartParameter startParameters = settings.getGradle().getStartParameter(); + final Object testLauncherArgsObject = startParameters.getProjectProperties().get( TEST_JDK_LAUNCHER_ARGS ); + final String testLauncherArgs = testLauncherArgsObject == null ? null : testLauncherArgsObject.toString(); final JdkVersionConfig jdkVersionConfig = createVersionConfig( explicitMainVersion, explicitTestVersion, gradleJdkVersion, baselineJdkVersion, - maxSupportedJdkVersion + maxSupportedJdkVersion, + testLauncherArgs ); settings.getGradle().getExtensions().add( JdkVersionConfig.DSL_NAME, jdkVersionConfig ); From 76027709b1e86b989ba13b91738553a6006f0d77 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Thu, 15 Feb 2024 11:56:32 +0100 Subject: [PATCH 148/321] HHH-17730 Add test for issue --- .../records/MergeRecordEmbeddedIdTest.java | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 hibernate-core/src/test/java17/org/hibernate/orm/test/records/MergeRecordEmbeddedIdTest.java diff --git a/hibernate-core/src/test/java17/org/hibernate/orm/test/records/MergeRecordEmbeddedIdTest.java b/hibernate-core/src/test/java17/org/hibernate/orm/test/records/MergeRecordEmbeddedIdTest.java new file mode 100644 index 000000000000..834d8fd97314 --- /dev/null +++ b/hibernate-core/src/test/java17/org/hibernate/orm/test/records/MergeRecordEmbeddedIdTest.java @@ -0,0 +1,148 @@ +package org.hibernate.orm.test.records; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ManyToOne; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * @author Marco Belladelli + */ +@DomainModel( annotatedClasses = MergeRecordEmbeddedIdTest.MyEntity.class ) +@SessionFactory +public class MergeRecordEmbeddedIdTest { + @AfterEach + protected void cleanupTest(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createMutationQuery( "delete from MyEntity" ).executeUpdate() ); + } + + @Test + public void mergeDetached(SessionFactoryScope scope) { + final MyRecord id = new MyRecord( 1, "entity_1" ); + scope.inTransaction( session -> session.persist( new MyEntity( id, "xyz" ) ) ); + scope.inTransaction( session -> session.merge( new MyEntity( + id, + "abc", + new MyEntity( new MyRecord( 2, "entity_2" ) ) + ) ) ); + scope.inSession( session -> { + final MyEntity result = session.find( MyEntity.class, id ); + assertEquals( "abc", result.getData() ); + assertEquals( 2, result.getAssociatedEntity().getRecord().code ); + assertEquals( "entity_2", result.getAssociatedEntity().getRecord().description ); + } ); + } + + @Test + public void mergeDetachedNullAssociation(SessionFactoryScope scope) { + final MyRecord id = new MyRecord( 1, "entity_1" ); + scope.inTransaction( session -> session.persist( new MyEntity( id ) ) ); + scope.inTransaction( session -> session.merge( new MyEntity( id, "abc" ) ) ); + scope.inSession( session -> { + final MyEntity result = session.find( MyEntity.class, id ); + assertEquals( "abc", result.getData() ); + assertNull( result.getAssociatedEntity() ); + } ); + } + + @Test + public void mergeTransient(SessionFactoryScope scope) { + final MyRecord id = new MyRecord( 1, "entity_1" ); + final MyEntity myEntity = new MyEntity( id, "abc" ); + myEntity.associatedEntity = new MyEntity( new MyRecord( 2, "entity_2" ), "xyz" ); + scope.inTransaction( session -> session.merge( myEntity ) ); + scope.inSession( session -> { + final MyEntity result = session.find( MyEntity.class, id ); + assertEquals( "abc", result.getData() ); + assertEquals( 2, result.getAssociatedEntity().getRecord().code ); + assertEquals( "entity_2", result.getAssociatedEntity().getRecord().description ); + assertEquals( "xyz", result.getAssociatedEntity().getData() ); + } ); + } + + @Test + public void mergeTransientNullAssociation(SessionFactoryScope scope) { + final MyRecord id = new MyRecord( 1, "entity_1" ); + final MyEntity myEntity = new MyEntity( id, "abc" ); + scope.inTransaction( session -> session.merge( myEntity ) ); + scope.inSession( session -> { + final MyEntity result = session.find( MyEntity.class, id ); + assertEquals( "abc", result.getData() ); + assertNull( result.getAssociatedEntity() ); + } ); + } + + @Test + public void mergePersistent(SessionFactoryScope scope) { + final MyRecord id = new MyRecord( 1, "entity_1" ); + scope.inTransaction( session -> { + final MyEntity entity = new MyEntity( id ); + session.persist( entity ); + entity.data = "abc"; + entity.associatedEntity = new MyEntity( new MyRecord( 2, "entity_2" ) ); + session.merge( entity ); + } ); + scope.inSession( session -> { + final MyEntity result = session.find( MyEntity.class, id ); + assertEquals( "abc", result.getData() ); + assertEquals( 2, result.getAssociatedEntity().getRecord().code ); + assertEquals( "entity_2", result.getAssociatedEntity().getRecord().description ); + } ); + } + + @Entity( name = "MyEntity" ) + public static class MyEntity { + @EmbeddedId + private MyRecord record; + + private String data; + + @ManyToOne( fetch = FetchType.LAZY, cascade = CascadeType.ALL ) + private MyEntity associatedEntity; + + public MyEntity() { + } + + public MyEntity(MyRecord record) { + this( record, null ); + } + + public MyEntity(MyRecord record, String data) { + this.record = record; + this.data = data; + } + + public MyEntity(MyRecord record, String data, MyEntity associatedEntity) { + this.record = record; + this.data = data; + this.associatedEntity = associatedEntity; + } + + public MyRecord getRecord() { + return record; + } + + public String getData() { + return data; + } + + public MyEntity getAssociatedEntity() { + return associatedEntity; + } + } + + @Embeddable + public record MyRecord(Integer code, String description) { + } +} From d29cb697f9e459fcc8cd1f422a66050af9d36707 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Thu, 15 Feb 2024 11:56:40 +0100 Subject: [PATCH 149/321] HHH-17730 Account for immutable embedded ids during merge --- .../hibernate/event/internal/DefaultMergeEventListener.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java index 83d686bc88d3..675c3de9ce45 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java @@ -264,8 +264,7 @@ else if ( subtype.isComponentType() ) { copyValues[i] = subtype.deepCopy( propertyValues[i], sessionFactory ); } } - compositeType.setPropertyValues( idCopy, copyValues ); - return idCopy; + return compositeType.replacePropertyValues( idCopy, copyValues, session ); } protected void entityIsPersistent(MergeEvent event, MergeContext copyCache) { From 23f5b45b5cab2dff45681569cce80f8949dc7918 Mon Sep 17 00:00:00 2001 From: Cedomir Igaly Date: Thu, 18 Jan 2024 18:01:42 +0100 Subject: [PATCH 150/321] HHH-17613 - Test case demonstrating the problem --- .../jpamodelgen/test/hhh17613/ChildB.java | 7 ++++++ .../test/hhh17613/HHH17613Test.java | 22 +++++++++++++++++++ .../jpamodelgen/test/hhh17613/Parent.java | 11 ++++++++++ .../jpamodelgen/test/hhh17613/a/ChildA.java | 18 +++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17613/ChildB.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17613/HHH17613Test.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17613/Parent.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17613/a/ChildA.java diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17613/ChildB.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17613/ChildB.java new file mode 100644 index 000000000000..bb7ef6f41134 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17613/ChildB.java @@ -0,0 +1,7 @@ +package org.hibernate.jpamodelgen.test.hhh17613; + +import jakarta.persistence.MappedSuperclass; + +@MappedSuperclass +public abstract class ChildB extends Parent { +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17613/HHH17613Test.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17613/HHH17613Test.java new file mode 100644 index 000000000000..36c004369eee --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17613/HHH17613Test.java @@ -0,0 +1,22 @@ +package org.hibernate.jpamodelgen.test.hhh17613; + +import org.hibernate.jpamodelgen.test.hhh17613.a.ChildA; +import org.hibernate.jpamodelgen.test.util.CompilationTest; +import org.hibernate.jpamodelgen.test.util.TestForIssue; +import org.hibernate.jpamodelgen.test.util.TestUtil; +import org.hibernate.jpamodelgen.test.util.WithClasses; + +import org.junit.Test; + +@TestForIssue(jiraKey = " HHH-17613") +public class HHH17613Test extends CompilationTest { + + @Test + @WithClasses({ ChildA.class, ChildB.class, Parent.class }) + @TestForIssue(jiraKey = " HHH-17613") + public void test() { + System.out.println( TestUtil.getMetaModelSourceAsString( ChildA.class ) ); + System.out.println( TestUtil.getMetaModelSourceAsString( ChildB.class ) ); + System.out.println( TestUtil.getMetaModelSourceAsString( Parent.class ) ); + } +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17613/Parent.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17613/Parent.java new file mode 100644 index 000000000000..2f04d9b48c7a --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17613/Parent.java @@ -0,0 +1,11 @@ +package org.hibernate.jpamodelgen.test.hhh17613; + +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; + +@MappedSuperclass +public abstract class Parent { + + @Id + private Long id; +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17613/a/ChildA.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17613/a/ChildA.java new file mode 100644 index 000000000000..bbea629e48d0 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17613/a/ChildA.java @@ -0,0 +1,18 @@ +package org.hibernate.jpamodelgen.test.hhh17613.a; + +import java.util.HashSet; +import java.util.Set; + +import org.hibernate.jpamodelgen.test.hhh17613.ChildB; +import org.hibernate.jpamodelgen.test.hhh17613.Parent; + +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.OneToMany; + +@MappedSuperclass +public abstract class ChildA> extends Parent { + + @OneToMany + private Set bs = new HashSet<>(); + +} From ce805efc0b52ba9173289321b255a46bf8f46cea Mon Sep 17 00:00:00 2001 From: Cedomir Igaly Date: Fri, 19 Jan 2024 15:52:04 +0100 Subject: [PATCH 151/321] HHH-17613 - Fixing type import --- .../jpamodelgen/ImportContextImpl.java | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/ImportContextImpl.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/ImportContextImpl.java index 0b8c106d3dcc..0d778f40c0b3 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/ImportContextImpl.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/ImportContextImpl.java @@ -11,6 +11,8 @@ import java.util.Objects; import java.util.Set; import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.hibernate.jpamodelgen.model.ImportContext; @@ -22,6 +24,8 @@ */ public class ImportContextImpl implements ImportContext { + private static final Pattern P_IMPORT = Pattern.compile( "(\\?\\s+\\w+\\s+)(.+)" ); + private final Set imports = new TreeSet<>(); private final Set staticImports = new TreeSet<>(); private final Map simpleNames = new HashMap<>(); @@ -66,13 +70,20 @@ public String importType(String typeExpression) { // strip off type annotations and '? super' or '? extends' String preamble = ""; - if ( result.startsWith("@") || result.startsWith("?") ) { + if ( result.startsWith( "@" ) ) { int index = result.lastIndexOf(' '); if ( index > 0 ) { preamble = result.substring( 0, index+1 ); result = result.substring( index+1 ); } } + else if ( result.startsWith( "?" ) ) { + final Matcher m = P_IMPORT.matcher( result ); + if ( m.matches() ) { + preamble = m.group( 1 ); + result = Objects.requireNonNullElse( m.group( 2 ), "" ); + } + } String appendices = ""; if ( result.indexOf( '<' ) >= 0 ) { @@ -123,11 +134,24 @@ private String unqualifyName(String qualifiedName) { private String importTypes(String originalArgList) { String[] args = originalArgList.split(","); StringBuilder argList = new StringBuilder(); + StringBuilder acc = new StringBuilder(); for ( String arg : args ) { + if ( acc.length() > 0 ) { + acc.append( ',' ); + } + acc.append( arg ); + final int count = acc.chars().reduce( + 0, + (left, right) -> left + ( right == '<' ? 1 : right == '>' ? -1 : 0 ) + ); + if ( count > 0 ) { + continue; + } if ( argList.length() > 0 ) { argList.append(','); } - argList.append( importType( arg ) ); + argList.append( importType( acc.toString() ) ); + acc.setLength( 0 ); } return argList.toString(); } From e427ff47a9a91f6a50a56dfc167c9b47bd15d20a Mon Sep 17 00:00:00 2001 From: Cedomir Igaly Date: Sun, 18 Feb 2024 13:03:15 +0100 Subject: [PATCH 152/321] HHH-17613 - Adding recursion; avoiding regular expression --- .../jpamodelgen/ImportContextImpl.java | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/ImportContextImpl.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/ImportContextImpl.java index 0d778f40c0b3..d20897c9ec49 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/ImportContextImpl.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/ImportContextImpl.java @@ -11,8 +11,6 @@ import java.util.Objects; import java.util.Set; import java.util.TreeSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.hibernate.jpamodelgen.model.ImportContext; @@ -24,8 +22,6 @@ */ public class ImportContextImpl implements ImportContext { - private static final Pattern P_IMPORT = Pattern.compile( "(\\?\\s+\\w+\\s+)(.+)" ); - private final Set imports = new TreeSet<>(); private final Set staticImports = new TreeSet<>(); private final Map simpleNames = new HashMap<>(); @@ -78,10 +74,26 @@ public String importType(String typeExpression) { } } else if ( result.startsWith( "?" ) ) { - final Matcher m = P_IMPORT.matcher( result ); - if ( m.matches() ) { - preamble = m.group( 1 ); - result = Objects.requireNonNullElse( m.group( 2 ), "" ); + int index = 1; + while ( index < result.length() && Character.isWhitespace( result.charAt( index ) ) ) { + index++; + } + if ( index < result.length() ) { + int nextIndex = -1; + if ( result.substring( index ).startsWith( "extends" ) ) { + nextIndex = index + 7; + } + else if ( result.substring( index ).startsWith( "super" ) ) { + nextIndex = index + 5; + } + if ( nextIndex > 0 && nextIndex < result.length() && Character.isWhitespace( result.charAt( nextIndex ) ) ) { + index = nextIndex; + while ( Character.isWhitespace( result.charAt( index ) ) ) { + index++; + } + preamble = result.substring( 0, index ); + result = importType( result.substring( index ) ); + } } } From b21786ace0cc7b590e5cf9db5047b7a6907ba3af Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 20 Feb 2024 10:10:15 +0100 Subject: [PATCH 153/321] HHH-17613 - don't use reduce() --- .../jpamodelgen/ImportContextImpl.java | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/ImportContextImpl.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/ImportContextImpl.java index d20897c9ec49..df856a2b2e6f 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/ImportContextImpl.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/ImportContextImpl.java @@ -10,10 +10,14 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.StringTokenizer; import java.util.TreeSet; import org.hibernate.jpamodelgen.model.ImportContext; +import static java.lang.Character.isWhitespace; +import static java.lang.System.lineSeparator; + /** * @author Max Andersen @@ -75,7 +79,7 @@ public String importType(String typeExpression) { } else if ( result.startsWith( "?" ) ) { int index = 1; - while ( index < result.length() && Character.isWhitespace( result.charAt( index ) ) ) { + while ( index < result.length() && isWhitespace( result.charAt( index ) ) ) { index++; } if ( index < result.length() ) { @@ -86,9 +90,9 @@ else if ( result.startsWith( "?" ) ) { else if ( result.substring( index ).startsWith( "super" ) ) { nextIndex = index + 5; } - if ( nextIndex > 0 && nextIndex < result.length() && Character.isWhitespace( result.charAt( nextIndex ) ) ) { + if ( nextIndex > 0 && nextIndex < result.length() && isWhitespace( result.charAt( nextIndex ) ) ) { index = nextIndex; - while ( Character.isWhitespace( result.charAt( index ) ) ) { + while ( isWhitespace( result.charAt( index ) ) ) { index++; } preamble = result.substring( 0, index ); @@ -144,26 +148,32 @@ private String unqualifyName(String qualifiedName) { } private String importTypes(String originalArgList) { - String[] args = originalArgList.split(","); StringBuilder argList = new StringBuilder(); StringBuilder acc = new StringBuilder(); - for ( String arg : args ) { + StringTokenizer args = new StringTokenizer( originalArgList, "," ); + while ( args.hasMoreTokens() ) { if ( acc.length() > 0 ) { acc.append( ',' ); } - acc.append( arg ); - final int count = acc.chars().reduce( - 0, - (left, right) -> left + ( right == '<' ? 1 : right == '>' ? -1 : 0 ) - ); - if ( count > 0 ) { - continue; + acc.append( args.nextToken() ); + int nesting = 0; + for ( int i = 0; i': + nesting--; + break; + } } - if ( argList.length() > 0 ) { - argList.append(','); + if ( nesting == 0 ) { + if ( argList.length() > 0 ) { + argList.append(','); + } + argList.append( importType( acc.toString() ) ); + acc.setLength( 0 ); } - argList.append( importType( acc.toString() ) ); - acc.setLength( 0 ); } return argList.toString(); } @@ -204,10 +214,10 @@ public String generateImports() { // don't add automatically "imported" stuff if ( !isAutoImported( next ) ) { if ( staticImports.contains( next ) ) { - builder.append( "import static " ).append( next ).append( ";" ).append( System.lineSeparator() ); + builder.append( "import static " ).append( next ).append( ";" ).append( lineSeparator() ); } else { - builder.append( "import " ).append( next ).append( ";" ).append( System.lineSeparator() ); + builder.append( "import " ).append( next ).append( ";" ).append( lineSeparator() ); } } } From 8215a28fcc42e563c940e55d89fe98a7cd9642f2 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 20 Feb 2024 10:26:06 +0100 Subject: [PATCH 154/321] HHH-17661 add test for issue --- .../jpamodelgen/test/hhh17661/Entity.java | 14 +++++++++++ .../test/hhh17661/HHH17661Test.java | 20 ++++++++++++++++ .../jpamodelgen/test/hhh17661/Tree.java | 23 +++++++++++++++++++ .../test/hhh17661/TreeRelation.java | 14 +++++++++++ 4 files changed, 71 insertions(+) create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17661/Entity.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17661/HHH17661Test.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17661/Tree.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17661/TreeRelation.java diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17661/Entity.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17661/Entity.java new file mode 100644 index 000000000000..3627d15b135e --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17661/Entity.java @@ -0,0 +1,14 @@ +package org.hibernate.jpamodelgen.test.hhh17661; + +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; + +import java.io.Serializable; + +@MappedSuperclass +public abstract class Entity implements Serializable { + + @Id + private Long id; + +} \ No newline at end of file diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17661/HHH17661Test.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17661/HHH17661Test.java new file mode 100644 index 000000000000..30655f88a4e7 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17661/HHH17661Test.java @@ -0,0 +1,20 @@ +package org.hibernate.jpamodelgen.test.hhh17661; + +import org.hibernate.jpamodelgen.test.util.CompilationTest; +import org.hibernate.jpamodelgen.test.util.TestForIssue; +import org.hibernate.jpamodelgen.test.util.TestUtil; +import org.hibernate.jpamodelgen.test.util.WithClasses; +import org.junit.Test; + +@TestForIssue(jiraKey = " HHH-17661") +public class HHH17661Test extends CompilationTest { + + @Test + @WithClasses({ Entity.class, Tree.class, TreeRelation.class }) + @TestForIssue(jiraKey = " HHH-17661") + public void test() { + System.out.println( TestUtil.getMetaModelSourceAsString( Entity.class ) ); + System.out.println( TestUtil.getMetaModelSourceAsString( Tree.class ) ); + System.out.println( TestUtil.getMetaModelSourceAsString( TreeRelation.class ) ); + } +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17661/Tree.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17661/Tree.java new file mode 100644 index 000000000000..e3e2110f48c8 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17661/Tree.java @@ -0,0 +1,23 @@ +package org.hibernate.jpamodelgen.test.hhh17661; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.FetchType; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.OneToMany; + +import java.util.HashSet; +import java.util.Set; + +@MappedSuperclass +public abstract class Tree, TR extends TreeRelation> extends Entity { + + @ManyToOne(fetch = FetchType.LAZY) + private T parent; + + @OneToMany(mappedBy = "parent") + private Set childRelation = new HashSet<>(); + + @OneToMany(mappedBy = "child", cascade = {CascadeType.ALL}, orphanRemoval = true) + private Set parentRelation = new HashSet<>(); +} \ No newline at end of file diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17661/TreeRelation.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17661/TreeRelation.java new file mode 100644 index 000000000000..4ec707ef170a --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/hhh17661/TreeRelation.java @@ -0,0 +1,14 @@ +package org.hibernate.jpamodelgen.test.hhh17661; + +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; + +@MappedSuperclass +public abstract class TreeRelation>> extends Entity { + + @ManyToOne(optional = false) + private T parent; + + @ManyToOne(optional = false) + private T child; +} \ No newline at end of file From 78cd0175cc2e8b3c8341e184a5b78b2ba0ec9f78 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 20 Feb 2024 11:42:36 +0100 Subject: [PATCH 155/321] HHH-17755 make @MapsId work correctly with @IdClass the @Id property was not getting populated --- .../boot/model/internal/BinderHelper.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java index 2cfa602dc0e6..f21bf4b36911 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java @@ -887,14 +887,16 @@ static PropertyData getPropertyOverriddenByMapperOrMapsId( .toXClass( propertyHolder.getPersistentClass().getMappedClass() ); final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector(); if ( propertyHolder.isInIdClass() ) { - final PropertyData propertyData = metadataCollector.getPropertyAnnotatedWithIdAndToOne( mappedClass, propertyName ); - return propertyData == null && buildingContext.getBuildingOptions().isSpecjProprietarySyntaxEnabled() - ? metadataCollector.getPropertyAnnotatedWithMapsId( mappedClass, propertyName ) - : propertyData; - } - else { - return metadataCollector.getPropertyAnnotatedWithMapsId( mappedClass, isId ? "" : propertyName ); + final PropertyData data = metadataCollector.getPropertyAnnotatedWithIdAndToOne( mappedClass, propertyName ); + if ( data != null ) { + return data; + } + // TODO: is this branch even necessary? + else if ( buildingContext.getBuildingOptions().isSpecjProprietarySyntaxEnabled() ) { + return metadataCollector.getPropertyAnnotatedWithMapsId( mappedClass, propertyName ); + } } + return metadataCollector.getPropertyAnnotatedWithMapsId( mappedClass, isId ? "" : propertyName ); } public static Map toAliasTableMap(SqlFragmentAlias[] aliases){ From d1db92c220a1f58586a1171e2de9481e0188a39c Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 20 Feb 2024 11:42:54 +0100 Subject: [PATCH 156/321] very minor code changes to ForeignGenerator --- .../java/org/hibernate/id/ForeignGenerator.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/id/ForeignGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/ForeignGenerator.java index 878547d83629..b02bf8746bf8 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/ForeignGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/ForeignGenerator.java @@ -87,11 +87,10 @@ public void configure(Type type, Properties parameters, ServiceRegistry serviceR @Override public Object generate(SharedSessionContractImplementor sessionImplementor, Object object) { - final EntityPersister entityDescriptor = sessionImplementor.getFactory() - .getRuntimeMetamodels() - .getMappingMetamodel() - .getEntityDescriptor( entityName ); - Object associatedObject = entityDescriptor.getPropertyValue( object, propertyName ); + final EntityPersister entityDescriptor = + sessionImplementor.getFactory().getMappingMetamodel() + .getEntityDescriptor( entityName ); + final Object associatedObject = entityDescriptor.getPropertyValue( object, propertyName ); if ( associatedObject == null ) { throw new IdentifierGenerationException( "attempted to assign id from null one-to-one property [" + getRole() + "]" @@ -111,7 +110,7 @@ public Object generate(SharedSessionContractImplementor sessionImplementor, Obje } Object id; - String associatedEntityName = foreignValueSourceType.getAssociatedEntityName(); + final String associatedEntityName = foreignValueSourceType.getAssociatedEntityName(); try { id = getEntityIdentifierIfNotUnsaved( associatedEntityName, associatedObject, sessionImplementor ); } @@ -133,7 +132,8 @@ else if ( sessionImplementor.isStatelessSession() ) { } } - if ( sessionImplementor.isSessionImplementor() && sessionImplementor.asSessionImplementor().contains( entityName, object ) ) { + if ( sessionImplementor.isSessionImplementor() + && sessionImplementor.asSessionImplementor().contains( entityName, object ) ) { //abort the save (the object is already saved by a circular cascade) return SHORT_CIRCUIT_INDICATOR; //throw new IdentifierGenerationException("save associated object first, or disable cascade for inverse association"); From 22b1d606c516c4e1010110638ca54ca0a0ccaec3 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 20 Feb 2024 13:27:34 +0100 Subject: [PATCH 157/321] fix spelling of method name --- .../org/hibernate/boot/model/internal/EmbeddableBinder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java index 674adad8f6a9..cb9679806569 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java @@ -367,7 +367,7 @@ static Component fillEmbeddable( if ( baseClassElements != null //useful to avoid breaking pre JPA 2 mappings && !hasAnnotationsOnIdClass( annotatedClass ) ) { - processIdClassElememts( propertyHolder, baseInferredData, classElements, baseClassElements ); + processIdClassElements( propertyHolder, baseInferredData, classElements, baseClassElements ); } for ( PropertyData propertyAnnotatedElement : classElements ) { processElementAnnotations( @@ -555,7 +555,7 @@ private static void processGeneratedId(MetadataBuildingContext context, Componen } } - private static void processIdClassElememts( + private static void processIdClassElements( PropertyHolder propertyHolder, PropertyData baseInferredData, List classElements, From e51d03c0ba7f185e4c1d5e35df700048f9fa5d5d Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 20 Feb 2024 13:28:44 +0100 Subject: [PATCH 158/321] HHH-17755 fix inferral of @IdClass column mapping from @MapsId --- .../boot/model/internal/PropertyBinder.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java index aff9efa64493..b57b2197adf4 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java @@ -1011,7 +1011,10 @@ private static AnnotatedColumns bindBasic( final boolean isComposite; final boolean isOverridden; final AnnotatedColumns actualColumns; - if ( propertyBinder.isId() || propertyHolder.isOrWithinEmbeddedId() || propertyHolder.isInIdClass() ) { + if ( isIdentifierMapper + || propertyBinder.isId() + || propertyHolder.isOrWithinEmbeddedId() + || propertyHolder.isInIdClass() ) { // the associated entity could be using an @IdClass making the overridden property a component final PropertyData overridingProperty = getPropertyOverriddenByMapperOrMapsId( propertyBinder.isId(), @@ -1021,8 +1024,7 @@ private static AnnotatedColumns bindBasic( ); if ( overridingProperty != null ) { isOverridden = true; - final InheritanceState state = inheritanceStatePerClass.get( overridingProperty.getClassOrElement() ); - isComposite = state != null ? state.hasIdClassOrEmbeddedId() : isEmbedded( property, returnedClass ); + isComposite = isComposite( inheritanceStatePerClass, property, returnedClass, overridingProperty ); //Get the new column actualColumns = columnsBuilder.overrideColumnFromMapperOrMapsIdProperty( propertyBinder.isId() ); } @@ -1102,6 +1104,15 @@ else if ( propertyBinder.isId() ) { return actualColumns; } + private static boolean isComposite( + Map inheritanceStatePerClass, + XProperty property, + XClass returnedClass, + PropertyData overridingProperty) { + final InheritanceState state = inheritanceStatePerClass.get( overridingProperty.getClassOrElement() ); + return state != null ? state.hasIdClassOrEmbeddedId() : isEmbedded( property, returnedClass ); + } + private static void handleGeneratorsForOverriddenId( PropertyHolder propertyHolder, Map classGenerators, From 3b9014cdbfd292705672ad36847bf711c480b201 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 20 Feb 2024 14:23:58 +0100 Subject: [PATCH 159/321] HHH-17755 add test for issue --- .../annotations/mapsid/MapsIdClassTest.java | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/mapsid/MapsIdClassTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/mapsid/MapsIdClassTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/mapsid/MapsIdClassTest.java new file mode 100644 index 000000000000..b4411ae112e3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/mapsid/MapsIdClassTest.java @@ -0,0 +1,104 @@ +package org.hibernate.orm.test.annotations.mapsid; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; +import jakarta.persistence.OneToMany; +import jakarta.persistence.PrimaryKeyJoinColumn; +import jakarta.persistence.Table; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; +import java.util.Set; + +import static jakarta.persistence.CascadeType.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@SessionFactory +@DomainModel(annotatedClasses ={MapsIdClassTest.User.class, MapsIdClassTest.UserAuthority.class}) +public class MapsIdClassTest { + + @Test + void test(SessionFactoryScope scope) { + scope.inTransaction( s-> { + User ue = new User(); + ue.setName("Gavin"); + UserAuthority uae = new UserAuthority(); + ue.addUserAuthority(uae); + uae.setUser(ue); + uae.setAuthority("God"); + s.persist(ue); + s.flush(); + assertEquals( ue.id, uae.userId ); + }); + + } + + static class UserAuthorityId { + private Long userId; + private String authority; + } + + @Entity + @Table(name = "users") + static class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + Long id; + + String name; + + String password; + + @Column(name = "is_enabled") + boolean enabled; + + public void setName(String name) { + this.name = name; + } + + @OneToMany( + cascade = {PERSIST, MERGE, REMOVE}, + mappedBy = "user", + orphanRemoval = true) + private Set userAuthorities = new HashSet<>(); + + public void addUserAuthority(UserAuthority userAuthority) { + this.userAuthorities.add(userAuthority); + } + } + + @Entity + @IdClass(UserAuthorityId.class) + @Table(name = "user_authorities") + static class UserAuthority { + + @Id + private Long userId; + + @Id + private String authority; + + @ManyToOne + @MapsId("userId") + @PrimaryKeyJoinColumn(name = "user_id") + private User user; + + public void setUser(User user) { + this.user = user; + } + + public void setAuthority(String authority) { + this.authority = authority; + } + } +} From aa15081c2a13f665d104a54bbbc76e7106c2ef41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 20 Feb 2024 15:45:25 +0100 Subject: [PATCH 160/321] Revert "Make sure test.jdk.launcher.args is passed as jvm args to test launcher" This reverts commit d7a46451ce7bc3ba3ce3cb0a7eada5ab824e4e1a. --- .../orm/toolchains/JavaModulePlugin.java | 5 ---- .../orm/toolchains/JdkVersionConfig.java | 24 +++++-------------- .../orm/toolchains/JdkVersionPlugin.java | 6 +---- .../toolchains/JdkVersionSettingsPlugin.java | 8 +------ 4 files changed, 8 insertions(+), 35 deletions(-) diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JavaModulePlugin.java b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JavaModulePlugin.java index 6be372d5b7bc..e197951b8dc1 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JavaModulePlugin.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JavaModulePlugin.java @@ -81,11 +81,6 @@ public void apply(Project project) { javaToolchainSpec.getLanguageVersion().set( jdkVersionsConfig.getTestLauncherVersion() ); } ) ); - - final String launcherArgs = jdkVersionsConfig.getTest().getLauncherArgs(); - if ( launcherArgs != null ) { - testTask.jvmArgs( (Object[]) launcherArgs.split( " " ) ); - } } } } diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionConfig.java b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionConfig.java index eb7a9582cec1..18020922f868 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionConfig.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionConfig.java @@ -30,7 +30,6 @@ public class JdkVersionConfig { public static final String DSL_NAME = "jdkVersions"; public static final String MAIN_JDK_VERSION = "main.jdk.version"; public static final String TEST_JDK_VERSION = "test.jdk.version"; - public static final String TEST_JDK_LAUNCHER_ARGS = "test.jdk.launcher.args"; private final boolean explicit; private final JavaLanguageVersion baseline; @@ -44,12 +43,11 @@ public JdkVersionConfig( JavaLanguageVersion mainReleaseVersion, JavaLanguageVersion testCompileVersion, JavaLanguageVersion testReleaseVersion, - JavaLanguageVersion testLauncherVersion, - String testLauncherArgs) { + JavaLanguageVersion testLauncherVersion) { this.explicit = explicit; this.baseline = baseline; this.main = new MainJdks( mainCompileVersion, mainReleaseVersion ); - this.test = new TestJdks( testCompileVersion, testReleaseVersion, testLauncherVersion, testLauncherArgs ); + this.test = new TestJdks( testCompileVersion, testReleaseVersion, testLauncherVersion ); } public boolean isExplicitlyConfigured() { @@ -112,8 +110,7 @@ public static JdkVersionConfig createVersionConfig( JavaLanguageVersion explicitTestVersion, JavaLanguageVersion gradleJdkVersion, JavaLanguageVersion baselineJdkVersion, - JavaLanguageVersion maxSupportedJdkVersion, - String testLauncherArgs) { + JavaLanguageVersion maxSupportedJdkVersion) { final boolean explicitlyConfigured = explicitMainVersion != null || explicitTestVersion != null; final JavaLanguageVersion mainCompileVersion; @@ -147,8 +144,7 @@ public static JdkVersionConfig createVersionConfig( mainReleaseVersion, testCompileVersion, testReleaseVersion, - testLauncherVersion, - testLauncherArgs + testLauncherVersion ); } else { @@ -173,8 +169,7 @@ public static JdkVersionConfig createVersionConfig( baselineJdkVersion, gradleJdkVersion, baselineJdkVersion, - gradleJdkVersion, - testLauncherArgs + gradleJdkVersion ); } } @@ -250,17 +245,14 @@ public static class TestJdks implements JdkVersionCombo { private final JavaLanguageVersion compileVersion; private final JavaLanguageVersion releaseVersion; private final JavaLanguageVersion launcherVersion; - private final String launcherArgs; public TestJdks( JavaLanguageVersion compileVersion, JavaLanguageVersion releaseVersion, - JavaLanguageVersion launcherVersion, - String launcherArgs) { + JavaLanguageVersion launcherVersion) { this.compileVersion = compileVersion; this.releaseVersion = releaseVersion; this.launcherVersion = launcherVersion; - this.launcherArgs = launcherArgs; } public JavaLanguageVersion getCompiler() { @@ -281,10 +273,6 @@ public JavaLanguageVersion getLauncher() { return launcherVersion; } - public String getLauncherArgs() { - return launcherArgs; - } - @Override public String toString() { return "[compile: " + compileVersion + ", release:" + releaseVersion + ", launcher: " + launcherVersion + "]"; diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionPlugin.java b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionPlugin.java index ee79cfe6eb5b..e9ad31604847 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionPlugin.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionPlugin.java @@ -26,7 +26,6 @@ import org.jetbrains.annotations.NotNull; import static org.hibernate.orm.toolchains.JdkVersionConfig.MAIN_JDK_VERSION; -import static org.hibernate.orm.toolchains.JdkVersionConfig.TEST_JDK_LAUNCHER_ARGS; import static org.hibernate.orm.toolchains.JdkVersionConfig.TEST_JDK_VERSION; import static org.hibernate.orm.toolchains.JdkVersionConfig.createVersionConfig; import static org.hibernate.orm.toolchains.JdkVersionConfig.extractVersion; @@ -52,16 +51,13 @@ public void apply(Project project) { final VersionCatalog jdkVersions = versionCatalogs.named( "jdks" ); final JavaLanguageVersion baselineJdkVersion = getJavaLanguageVersion( jdkVersions, "baseline" ); final JavaLanguageVersion maxSupportedJdkVersion = getJavaLanguageVersion( jdkVersions, "maxSupportedBytecode" ); - final Object testLauncherArgsObject = project.getProperties().get( TEST_JDK_LAUNCHER_ARGS ); - final String testLauncherArgs = testLauncherArgsObject == null ? null : testLauncherArgsObject.toString(); final JdkVersionConfig jdkVersionConfig = createVersionConfig( explicitMainVersion, explicitTestVersion, gradleJdkVersion, baselineJdkVersion, - maxSupportedJdkVersion, - testLauncherArgs + maxSupportedJdkVersion ); project.getExtensions().add( JdkVersionConfig.DSL_NAME, jdkVersionConfig ); diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionSettingsPlugin.java b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionSettingsPlugin.java index 245f19ead5e3..66b3f19dbd1f 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionSettingsPlugin.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JdkVersionSettingsPlugin.java @@ -6,7 +6,6 @@ */ package org.hibernate.orm.toolchains; -import org.gradle.StartParameter; import org.gradle.api.JavaVersion; import org.gradle.api.Plugin; import org.gradle.api.artifacts.VersionCatalog; @@ -18,7 +17,6 @@ import org.jetbrains.annotations.NotNull; import static org.hibernate.orm.toolchains.JdkVersionConfig.MAIN_JDK_VERSION; -import static org.hibernate.orm.toolchains.JdkVersionConfig.TEST_JDK_LAUNCHER_ARGS; import static org.hibernate.orm.toolchains.JdkVersionConfig.TEST_JDK_VERSION; import static org.hibernate.orm.toolchains.JdkVersionConfig.createVersionConfig; import static org.hibernate.orm.toolchains.JdkVersionConfig.extractVersion; @@ -41,17 +39,13 @@ public void apply(Settings settings) { // maxSupportedJdkVersion = getJavaLanguageVersion( jdkVersions, "maxSupportedBytecode" ); baselineJdkVersion = JavaLanguageVersion.of( "11" ); maxSupportedJdkVersion = JavaLanguageVersion.of( "17" ); - final StartParameter startParameters = settings.getGradle().getStartParameter(); - final Object testLauncherArgsObject = startParameters.getProjectProperties().get( TEST_JDK_LAUNCHER_ARGS ); - final String testLauncherArgs = testLauncherArgsObject == null ? null : testLauncherArgsObject.toString(); final JdkVersionConfig jdkVersionConfig = createVersionConfig( explicitMainVersion, explicitTestVersion, gradleJdkVersion, baselineJdkVersion, - maxSupportedJdkVersion, - testLauncherArgs + maxSupportedJdkVersion ); settings.getGradle().getExtensions().add( JdkVersionConfig.DSL_NAME, jdkVersionConfig ); From b7464668961e6c5e1fa952a0e7826fa5b7f20114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 20 Feb 2024 15:50:39 +0100 Subject: [PATCH 161/321] Fix quoting problem in Jenkinsfile --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index bc433e6b071e..ae684081698e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -122,7 +122,7 @@ stage('Build') { } if ( buildEnv.testJdkLauncherArgs ) { state[buildEnv.tag]['additionalOptions'] = state[buildEnv.tag]['additionalOptions'] + - " -Ptest.jdk.launcher.args=${buildEnv.testJdkLauncherArgs}" + " -Ptest.jdk.launcher.args='${buildEnv.testJdkLauncherArgs}'" } state[buildEnv.tag]['containerName'] = null; stage('Checkout') { From 104eb3cf32c62fa035d046962e1a5a780d9ccb24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 20 Feb 2024 16:38:13 +0100 Subject: [PATCH 162/321] Actually set JVM options in JavaModulePlugin The previous implementation was not doing anything: somehow Gradle was erasing any change. Most likely the list passed to setJvmArgs is getting copied and later changes to that list are just ignored. --- .../orm/toolchains/JavaModulePlugin.java | 45 ++++++------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JavaModulePlugin.java b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JavaModulePlugin.java index e197951b8dc1..983c8ceb0f28 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JavaModulePlugin.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/toolchains/JavaModulePlugin.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import javax.inject.Inject; @@ -20,6 +21,7 @@ import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.SourceSetContainer; import org.gradle.api.tasks.compile.CompileOptions; +import org.gradle.api.tasks.compile.ForkOptions; import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.api.tasks.javadoc.Javadoc; import org.gradle.api.tasks.testing.Test; @@ -89,18 +91,15 @@ private void configureCompileTask(JavaCompile compileTask, JavaLanguageVersion r final CompileOptions compileTaskOptions = compileTask.getOptions(); compileTaskOptions.getRelease().set( releaseVersion.asInt() ); // Needs add-opens because of https://github.com/gradle/gradle/issues/15538 - compileTaskOptions.getForkOptions().getJvmArgs().add( "--add-opens" ); - compileTaskOptions.getForkOptions().getJvmArgs().add( "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED" ); + addJvmArgs( compileTask, "--add-opens", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED" ); } private void configureCompileTasks(Project project) { project.getTasks().withType( JavaCompile.class ).configureEach( new Action() { @Override public void execute(JavaCompile compileTask) { - getJvmArgs( compileTask ).addAll( - Arrays.asList( - project.property( "toolchain.compiler.jvmargs" ).toString().split( " " ) - ) + addJvmArgs( compileTask, + project.property( "toolchain.compiler.jvmargs" ).toString().split( " " ) ); compileTask.doFirst( new Action() { @@ -121,13 +120,13 @@ private void configureTestTasks(Project project) { project.getTasks().withType( Test.class ).configureEach( new Action() { @Override public void execute(Test testTask) { - getJvmArgs( testTask ).addAll( + testTask.jvmArgs( Arrays.asList( project.property( "toolchain.launcher.jvmargs" ).toString().split( " " ) ) ); if ( project.hasProperty( "test.jdk.launcher.args" ) ) { - getJvmArgs( testTask ).addAll( + testTask.jvmArgs( Arrays.asList( project.getProperties().get( "test.jdk.launcher.args" ).toString().split( " " ) ) @@ -169,30 +168,12 @@ private static List javadocFlags(Project project) { return Arrays.asList( splits ).stream().filter( (split) -> !split.isEmpty() ).collect( Collectors.toList() ); } - public static List getJvmArgs(JavaCompile compileTask) { - final List existing = compileTask + public static void addJvmArgs(JavaCompile compileTask, String ... newArgs) { + ForkOptions forOptions = compileTask .getOptions() - .getForkOptions() - .getJvmArgs(); - if ( existing == null ) { - final List target = new ArrayList<>(); - compileTask.getOptions().getForkOptions().setJvmArgs( target ); - return target; - } - else { - return existing; - } - } - - public static List getJvmArgs(Test testTask) { - final List existing = testTask.getJvmArgs(); - if ( existing == null || !( existing instanceof ArrayList ) ) { - final List target = new ArrayList<>(); - testTask.setJvmArgs( target ); - return target; - } - else { - return existing; - } + .getForkOptions(); + final List mergedArgs = new ArrayList<>( forOptions.getJvmArgs() ); + Collections.addAll( mergedArgs, newArgs ); + forOptions.setJvmArgs( mergedArgs ); } } From 13678e412418258dd749d061f8f1a28c38b5e6bb Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Wed, 21 Feb 2024 14:23:16 +0100 Subject: [PATCH 163/321] HHH-17765 Support some special chars in query method queries --- .../annotation/AnnotationMetaEntity.java | 3 +- .../jpamodelgen/annotation/QueryMethod.java | 32 +++++++++++++++---- .../hibernate/jpamodelgen/test/dao/Dao.java | 3 ++ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java index 61b9dd4f8acd..bec16ff27ad8 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Map; import java.util.StringTokenizer; +import java.util.regex.Pattern; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; @@ -1457,7 +1458,7 @@ else if ( !context.getTypeUtils().isSameType(typeArgument, returnType) ) { } private static boolean parameterIsMissing(String hql, int i, String param, String type) { - return !hql.matches(".*(:" + param + "|\\?" + i + ")\\b.*") + return !Pattern.compile( ".*(:" + param + "|\\?" + i + ")\\b.*", Pattern.DOTALL ).matcher( hql ).matches() && !isSessionParameter(type) && !isPageParam(type) && !isOrderParam(type); diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/QueryMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/QueryMethod.java index 8fa7f972cc53..1cca4ab1b74a 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/QueryMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/QueryMethod.java @@ -241,13 +241,31 @@ private void modifiers(List paramTypes, StringBuilder declaration) { @Override public String getAttributeNameDeclarationString() { - return new StringBuilder() - .append("static final String ") - .append(getConstantName()) - .append(" = \"") - .append(queryString) - .append("\";") - .toString(); + StringBuilder sb = new StringBuilder(queryString.length() + 100) + .append( "static final String " ) + .append( getConstantName() ) + .append( " = \"" ); + for ( int i = 0; i < queryString.length(); i++ ) { + final char c = queryString.charAt( i ); + switch ( c ) { + case '\r': + sb.append( "\\r" ); + break; + case '\n': + sb.append( "\\n" ); + break; + case '\\': + sb.append( "\\\\" ); + break; + case '"': + sb.append( "\\\"" ); + break; + default: + sb.append( c ); + break; + } + } + return sb.append("\";").toString(); } private String getConstantName() { diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/dao/Dao.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/dao/Dao.java index 56d95ff58795..419845387318 100644 --- a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/dao/Dao.java +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/dao/Dao.java @@ -112,4 +112,7 @@ class Record { @HQL("select avg(pages) from Book") double averagePageCount(); + + @HQL("select b\nfrom Book b\nwhere b.isbn = :isbn") + Book findByIsbnMultiline(String isbn); } From 5efb33b5a20db86eef54a41f01a2fd6cee4b1ce6 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Wed, 21 Feb 2024 18:27:21 +0100 Subject: [PATCH 164/321] HHH-17734 Ensure provider_class has precedence over datasource --- .../connections/internal/ConnectionProviderInitiator.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/ConnectionProviderInitiator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/ConnectionProviderInitiator.java index f14c04cb9679..3dd388a77381 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/ConnectionProviderInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/ConnectionProviderInitiator.java @@ -104,10 +104,6 @@ public ConnectionProvider initiateService( return null; } - if ( configurationValues.get( AvailableSettings.DATASOURCE ) != null ) { - return new DatasourceConnectionProviderImpl(); - } - final StrategySelector strategySelector = registry.getService( StrategySelector.class ); final Object explicitSetting = configurationValues.get( AvailableSettings.CONNECTION_PROVIDER ); if ( explicitSetting != null ) { @@ -150,6 +146,10 @@ else if ( explicitSetting instanceof Class ) { } } + if ( configurationValues.get( AvailableSettings.DATASOURCE ) != null ) { + return new DatasourceConnectionProviderImpl(); + } + ConnectionProvider connectionProvider = null; final Class singleRegisteredProvider = getSingleRegisteredProvider( From 6eadbde9ebba5f739f8f9a7bebad071317dda9a0 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Tue, 20 Feb 2024 12:41:00 +0100 Subject: [PATCH 165/321] HHH-17750 Add a reproducer --- .../lazy/LazyProxyWithCollectionTest.java | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyProxyWithCollectionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyProxyWithCollectionTest.java index e589476b4376..df0995a97e24 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyProxyWithCollectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/LazyProxyWithCollectionTest.java @@ -9,18 +9,9 @@ import java.util.HashSet; import java.util.Set; -import org.hibernate.HibernateException; -import org.hibernate.bytecode.enhance.spi.UnloadedClass; -import org.hibernate.event.service.spi.EventListenerRegistry; -import org.hibernate.event.spi.EventType; -import org.hibernate.event.spi.LoadEvent; -import org.hibernate.event.spi.LoadEventListener; - -import org.hibernate.testing.TestForIssue; import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; -import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext; -import org.hibernate.testing.bytecode.enhancement.EnhancerTestContext; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.orm.junit.Jira; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -35,12 +26,13 @@ import jakarta.persistence.Table; import jakarta.persistence.Version; +import static org.assertj.core.api.Assertions.assertThat; import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; /** * @author Christian Beikov */ -@TestForIssue( jiraKey = "HHH-14619" ) +@Jira( "https://hibernate.atlassian.net/browse/HHH-14619" ) @RunWith( BytecodeEnhancerRunner.class ) public class LazyProxyWithCollectionTest extends BaseCoreFunctionalTestCase { @@ -85,6 +77,28 @@ public void testLazyCollection() { } ); } + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-17750" ) + public void testMerge() { + final Child child = doInJPA( this::sessionFactory, em -> { + return em.find( Child.class, childId ); + } ); + + final Parent parent = doInJPA( this::sessionFactory, em -> { + Parent p = new Parent(); + p.setChild( child ); + return em.merge( p ); + } ); + + doInJPA( this::sessionFactory, em -> { + em.merge( parent ); + } ); + + doInJPA( this::sessionFactory, em -> { + assertThat( em.find( Parent.class, parent.getId() ).getChild().getId() ).isEqualTo( child.getId() ); + } ); + } + // --- // @Entity From 62e6732206e014028bd705b0d082c61e15dc8ba0 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 21 Feb 2024 11:33:16 +0100 Subject: [PATCH 166/321] HHH-17750 Handle bytecode-enhanced proxies in context identifier --- .../java/org/hibernate/internal/SessionImpl.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index b202d1d00e53..604e1effa40e 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -49,6 +49,7 @@ import org.hibernate.TypeMismatchException; import org.hibernate.UnknownProfileException; import org.hibernate.UnresolvableObjectException; +import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.internal.StatefulPersistenceContext; import org.hibernate.engine.jdbc.LobCreator; @@ -60,6 +61,7 @@ import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -160,6 +162,8 @@ import static org.hibernate.cfg.AvailableSettings.JPA_SHARED_CACHE_STORE_MODE; import static org.hibernate.cfg.AvailableSettings.STATEMENT_BATCH_SIZE; import static org.hibernate.cfg.AvailableSettings.USE_SUBSELECT_FETCH; +import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; +import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable; import static org.hibernate.event.spi.LoadEventListener.IMMEDIATE_LOAD; import static org.hibernate.event.spi.LoadEventListener.INTERNAL_LOAD_EAGER; import static org.hibernate.event.spi.LoadEventListener.INTERNAL_LOAD_LAZY; @@ -1529,10 +1533,16 @@ public Object getContextEntityIdentifier(Object object) { if ( lazyInitializer != null ) { return lazyInitializer.getInternalIdentifier(); } - else { - final EntityEntry entry = persistenceContext.getEntry( object ); - return entry != null ? entry.getId() : null; + else if ( isPersistentAttributeInterceptable( object ) ) { + final PersistentAttributeInterceptor interceptor = + asPersistentAttributeInterceptable( object ).$$_hibernate_getInterceptor(); + if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) { + return ( (EnhancementAsProxyLazinessInterceptor) interceptor ).getIdentifier(); + } } + + final EntityEntry entry = persistenceContext.getEntry( object ); + return entry != null ? entry.getId() : null; } @Override From b139d449c86538ab86b4113fdaaf0fa5435cf215 Mon Sep 17 00:00:00 2001 From: datazuul Date: Fri, 23 Feb 2024 13:43:38 +0100 Subject: [PATCH 167/321] HHH-17275: Fix NPE in BooleanJavaType for converter returning NULL for relational value --- .../java/org/hibernate/dialect/Dialect.java | 45 ++++++- .../type/descriptor/java/BooleanJavaType.java | 14 ++- .../java/BooleanJavaTypeDescriptorTest.java | 119 +++++++++++++++++- 3 files changed, 168 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 4d041cd6ba1b..e89d6e9ab90f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -808,7 +808,8 @@ public String[] getDropEnumTypeCommand(Class> enumType) { /** * Render a SQL check condition for a column that represents an enumerated value - * by its {@linkplain jakarta.persistence.EnumType#STRING string representation}. + * by its {@linkplain jakarta.persistence.EnumType#STRING string representation} + * or a given list of values (with NULL value allowed). * * @return a SQL expression that will occur in a {@code check} constraint */ @@ -816,11 +817,20 @@ public String getCheckCondition(String columnName, String[] values) { StringBuilder check = new StringBuilder(); check.append( columnName ).append( " in (" ); String separator = ""; + boolean nullIsValid = false; for ( String value : values ) { + if ( value == null ) { + nullIsValid = true; + continue; + } check.append( separator ).append('\'').append( value ).append('\''); separator = ","; } - return check.append( ')' ).toString(); + check.append( ')' ); + if ( nullIsValid ) { + check.append( " or " ).append( columnName ).append( " is null" ); + } + return check.toString(); } public String getCheckCondition(String columnName, Class> enumType) { @@ -842,16 +852,43 @@ public String getCheckCondition(String columnName, long min, long max) { * by its {@linkplain jakarta.persistence.EnumType#ORDINAL ordinal representation}. * * @return a SQL expression that will occur in a {@code check} constraint + * @deprecated use {@link #getCheckCondition(String, Long[])} instead */ + @Deprecated(forRemoval = true) public String getCheckCondition(String columnName, long[] values) { + Long objValues [] = new Long[ values.length ]; + int i = 0; + for( long temp : values){ + objValues[ i++ ] = temp; + } + return getCheckCondition(columnName, objValues); + } + + /** + * Render a SQL check condition for a column that represents an enumerated value + * by its {@linkplain jakarta.persistence.EnumType#ORDINAL ordinal representation} + * or a given list of values. + * + * @return a SQL expression that will occur in a {@code check} constraint + */ + public String getCheckCondition(String columnName, Long[] values) { StringBuilder check = new StringBuilder(); check.append( columnName ).append( " in (" ); String separator = ""; - for ( long value : values ) { + boolean nullIsValid = false; + for ( Long value : values ) { + if ( value == null ) { + nullIsValid = true; + continue; + } check.append( separator ).append( value ); separator = ","; } - return check.append( ')' ).toString(); + check.append( ')' ); + if ( nullIsValid ) { + check.append( " or " ).append( columnName ).append( " is null" ); + } + return check.toString(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanJavaType.java index b83a44643b04..e8487b35e727 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanJavaType.java @@ -191,9 +191,11 @@ public String getCheckCondition(String columnName, JdbcType jdbcType, BasicValue @SuppressWarnings("unchecked") BasicValueConverter stringConverter = (BasicValueConverter) converter; + final Object falseValue = stringConverter.toRelationalValue( false ); + final Object trueValue = stringConverter.toRelationalValue( true ); String[] values = new String[] { - stringConverter.toRelationalValue(false).toString(), - stringConverter.toRelationalValue(true).toString() + falseValue != null ? falseValue.toString() : null, + trueValue != null ? trueValue.toString() : null }; return dialect.getCheckCondition( columnName, values ); } @@ -201,9 +203,11 @@ else if ( jdbcType.isInteger() ) { @SuppressWarnings("unchecked") BasicValueConverter numericConverter = (BasicValueConverter) converter; - long[] values = new long[] { - numericConverter.toRelationalValue(false).longValue(), - numericConverter.toRelationalValue(true).longValue() + final Number falseValue = numericConverter.toRelationalValue( false ); + final Number trueValue = numericConverter.toRelationalValue( true ); + Long[] values = new Long[] { + falseValue != null ? Long.valueOf(falseValue.longValue()) : null, + trueValue != null ? Long.valueOf(trueValue.longValue()) : null }; return dialect.getCheckCondition( columnName, values ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/BooleanJavaTypeDescriptorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/BooleanJavaTypeDescriptorTest.java index 967f8041e303..df7831d4f522 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/BooleanJavaTypeDescriptorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/BooleanJavaTypeDescriptorTest.java @@ -6,8 +6,19 @@ */ package org.hibernate.orm.test.mapping.type.java; +import jakarta.persistence.AttributeConverter; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.dialect.Dialect; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.type.NumericBooleanConverter; +import org.hibernate.type.TrueFalseConverter; +import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; import org.hibernate.type.descriptor.java.BooleanJavaType; - +import org.hibernate.type.descriptor.java.IntegerJavaType; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.java.StringJavaType; +import org.hibernate.type.descriptor.jdbc.IntegerJdbcType; +import org.hibernate.type.descriptor.jdbc.VarcharJdbcType; import org.junit.Test; import static org.junit.Assert.*; @@ -15,6 +26,43 @@ public class BooleanJavaTypeDescriptorTest { private BooleanJavaType underTest = new BooleanJavaType(); + @Test + @JiraKey( "HHH-17275" ) + public void testCheckConditionShouldReturnCorrectStatementWhenNullXStringGiven() { + // given + // when + String checkCondition = underTest.getCheckCondition("is_active", VarcharJdbcType.INSTANCE, new BooleanXConverter(), new AnyDialect()); + // then + assertEquals("is_active in ('X') or is_active is null", checkCondition); + } + + @Test + public void testCheckConditionShouldReturnCorrectStatementWhenTFStringGiven() { + // given + // when + String checkCondition = underTest.getCheckCondition("is_active", VarcharJdbcType.INSTANCE, new TrueFalseConverter(), new AnyDialect()); + // then + assertEquals("is_active in ('F','T')", checkCondition); + } + + @Test + public void testCheckConditionShouldReturnCorrectStatementWhen1AndNullIntegerGiven() { + // given + // when + String checkCondition = underTest.getCheckCondition("is_active", IntegerJdbcType.INSTANCE, new OneNullBooleanConverter(), new AnyDialect()); + // then + assertEquals("is_active in (1) or is_active is null", checkCondition); + } + + @Test + public void testCheckConditionShouldReturnCorrectStatementWhen1And0IntegerGiven() { + // given + // when + String checkCondition = underTest.getCheckCondition("is_active", IntegerJdbcType.INSTANCE, new NumericBooleanConverter(), new AnyDialect()); + // then + assertEquals("is_active in (0,1)", checkCondition); + } + @Test public void testWrapShouldReturnTrueWhenYStringGiven() { // given @@ -59,4 +107,73 @@ public void testWrapShouldReturnFalseWhenEmptyStringGiven() { // then assertFalse(result); } + + private static class AnyDialect extends Dialect { + + } + + private static class BooleanXConverter implements AttributeConverter, BasicValueConverter { + + @Override + public String convertToDatabaseColumn(Boolean value) { + return value != null && value ? "X" : null; + } + + @Override + public Boolean convertToEntityAttribute(String value) { + return value != null; + } + + @Override + public @Nullable Boolean toDomainValue(@Nullable String relationalForm) { + return convertToEntityAttribute(relationalForm); + } + + @Override + public @Nullable String toRelationalValue(@Nullable Boolean domainForm) { + return convertToDatabaseColumn(domainForm); + } + + @Override + public JavaType getDomainJavaType() { + return BooleanJavaType.INSTANCE; + } + + @Override + public JavaType getRelationalJavaType() { + return StringJavaType.INSTANCE; + } + } + + private static class OneNullBooleanConverter implements AttributeConverter, BasicValueConverter { + @Override + public Integer convertToDatabaseColumn(Boolean attribute) { + return attribute != null && attribute ? 1 : null; + } + + @Override + public Boolean convertToEntityAttribute(Integer dbData) { + return dbData != null && dbData == 1; + } + + @Override + public @Nullable Boolean toDomainValue(@Nullable Integer relationalForm) { + return convertToEntityAttribute(relationalForm); + } + + @Override + public @Nullable Integer toRelationalValue(@Nullable Boolean domainForm) { + return convertToDatabaseColumn(domainForm); + } + + @Override + public JavaType getDomainJavaType() { + return BooleanJavaType.INSTANCE; + } + + @Override + public JavaType getRelationalJavaType() { + return IntegerJavaType.INSTANCE; + } + } } \ No newline at end of file From 3f6090996736510c91a754589228140c209e2d4a Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 13 Feb 2024 11:13:23 +0100 Subject: [PATCH 168/321] HHH-17726 Add test for issue --- .../mapping/basic/TimeZoneMappingTests.java | 28 +++++++++++-- .../mapping/basic/ZoneOffsetMappingTests.java | 31 +++++++++++++-- .../mapping/type/java/ZoneMappingTests.java | 39 +++++++++++++------ 3 files changed, 81 insertions(+), 17 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/TimeZoneMappingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/TimeZoneMappingTests.java index ab45c0a965f6..9067ee8e7d08 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/TimeZoneMappingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/TimeZoneMappingTests.java @@ -8,9 +8,6 @@ import java.sql.Types; import java.util.TimeZone; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; @@ -19,10 +16,16 @@ import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @@ -57,6 +60,25 @@ public void verifyMappings(SessionFactoryScope scope) { ); } + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-17726" ) + public void testUpdateQuery(SessionFactoryScope scope) { + scope.inTransaction( session -> session.persist( new EntityWithTimeZone( 1, TimeZone.getDefault() ) ) ); + scope.inTransaction( session -> { + final TimeZone timeZone = TimeZone.getTimeZone( "UTC" ); + session.createMutationQuery( "update EntityWithTimeZone e set e.timeZone = :timeZone" ) + .setParameter( "timeZone", timeZone ) + .executeUpdate(); + final EntityWithTimeZone entity = session.find( EntityWithTimeZone.class, 1 ); + assertThat( entity.timeZone, equalTo( timeZone ) ); + } ); + } + + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createMutationQuery( "delete EntityWithTimeZone" ).executeUpdate() ); + } + @Entity(name = "EntityWithTimeZone") @Table(name = "EntityWithTimeZone") public static class EntityWithTimeZone { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/ZoneOffsetMappingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/ZoneOffsetMappingTests.java index 2f30c2fd4d46..7667ebb7398d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/ZoneOffsetMappingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/ZoneOffsetMappingTests.java @@ -8,9 +8,6 @@ import java.sql.Types; import java.time.ZoneOffset; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; @@ -19,10 +16,16 @@ import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @@ -57,6 +60,28 @@ public void verifyMappings(SessionFactoryScope scope) { ); } + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-17726" ) + public void testUpdateQuery(SessionFactoryScope scope) { + scope.inTransaction( session -> session.persist( new EntityWithZoneOffset( + 1, + ZoneOffset.from( ZoneOffset.MIN ) + ) ) ); + scope.inTransaction( session -> { + final ZoneOffset zoneOffset = ZoneOffset.from( ZoneOffset.UTC ); + session.createMutationQuery( "update EntityWithZoneOffset e set e.zoneOffset = :zoneOffset" ) + .setParameter( "zoneOffset", zoneOffset ) + .executeUpdate(); + final EntityWithZoneOffset entity = session.find( EntityWithZoneOffset.class, 1 ); + assertThat( entity.zoneOffset, equalTo( zoneOffset ) ); + } ); + } + + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createMutationQuery( "delete EntityWithZoneOffset" ).executeUpdate() ); + } + @Entity(name = "EntityWithZoneOffset") @Table(name = "EntityWithZoneOffset") public static class EntityWithZoneOffset { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/ZoneMappingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/ZoneMappingTests.java index 886c435215da..94860eca83b7 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/ZoneMappingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/ZoneMappingTests.java @@ -18,10 +18,11 @@ import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; - +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import jakarta.persistence.CollectionTable; @@ -70,18 +71,34 @@ public void testUsage(SessionFactoryScope scope) { session.persist( entity3 ); } ); - try { - scope.inTransaction( (session) -> { - session.createQuery( "from ZoneMappingTestEntity" ).list(); - }); - } - finally { - scope.inTransaction( (session) -> { - session.createQuery( "delete ZoneMappingTestEntity" ).executeUpdate(); - }); - } + scope.inTransaction( (session) -> session.createQuery( "from ZoneMappingTestEntity" ).list() ); + } + + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-17726" ) + public void testUpdateQuery(SessionFactoryScope scope) { + scope.inTransaction( session -> session.persist( new ZoneMappingTestEntity( + 1, + "one", + ZoneId.systemDefault(), + ZoneOffset.MIN + ) ) ); + scope.inTransaction( session -> { + final ZoneId zoneId = ZoneId.of( "UTC" ); + final ZoneOffset zoneOffset = ZoneOffset.from( ZoneOffset.UTC ); + session.createMutationQuery( + "update ZoneMappingTestEntity e set e.zoneId = :zoneId, e.zoneOffset = :zoneOffset" + ).setParameter( "zoneId", zoneId ).setParameter( "zoneOffset", zoneOffset ).executeUpdate(); + final ZoneMappingTestEntity entity = session.find( ZoneMappingTestEntity.class, 1 ); + assertThat( entity.getZoneId() ).isEqualTo( zoneId ); + assertThat( entity.getZoneOffset() ).isEqualTo( zoneOffset ); + } ); } + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createMutationQuery( "delete ZoneMappingTestEntity" ).executeUpdate() ); + } @Entity( name = "ZoneMappingTestEntity" ) @Table( name = "zone_map_test_entity" ) From 0286006991e03fa6b96c31663b2c13e526690434 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 13 Feb 2024 11:13:31 +0100 Subject: [PATCH 169/321] HHH-17726 Cleanup java types missing same type in wrap/unwrap --- .../type/descriptor/java/TimeZoneJavaType.java | 8 +++++++- .../hibernate/type/descriptor/java/ZoneIdJavaType.java | 10 ++++++---- .../type/descriptor/java/ZoneOffsetJavaType.java | 6 ++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/TimeZoneJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/TimeZoneJavaType.java index 765f6b7eb3fb..d9360891ec20 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/TimeZoneJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/TimeZoneJavaType.java @@ -44,6 +44,9 @@ public X unwrap(TimeZone value, Class type, WrapperOptions options) { if ( value == null ) { return null; } + if ( TimeZone.class.isAssignableFrom( type ) ) { + return (X) value; + } if ( String.class.isAssignableFrom( type ) ) { return (X) toString( value ); } @@ -54,7 +57,10 @@ public TimeZone wrap(X value, WrapperOptions options) { if ( value == null ) { return null; } - if (value instanceof CharSequence) { + if ( value instanceof TimeZone ) { + return (TimeZone) value; + } + if ( value instanceof CharSequence ) { return fromString( (CharSequence) value ); } throw unknownWrap( value.getClass() ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZoneIdJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZoneIdJavaType.java index c5d2f59a0539..e447590b1d49 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZoneIdJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZoneIdJavaType.java @@ -47,11 +47,12 @@ public X unwrap(ZoneId value, Class type, WrapperOptions options) { if ( value == null ) { return null; } - + if ( ZoneId.class.isAssignableFrom( type ) ) { + return (X) value; + } if ( String.class.isAssignableFrom( type ) ) { return (X) toString( value ); } - throw unknownUnwrap( type ); } @@ -60,11 +61,12 @@ public ZoneId wrap(X value, WrapperOptions options) { if ( value == null ) { return null; } - + if ( value instanceof ZoneId ) { + return (ZoneId) value; + } if ( value instanceof String ) { return fromString( (String) value ); } - throw unknownWrap( value.getClass() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZoneOffsetJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZoneOffsetJavaType.java index 0c38dbd89aea..f98beaf1a99d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZoneOffsetJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZoneOffsetJavaType.java @@ -53,6 +53,9 @@ public X unwrap(ZoneOffset value, Class type, WrapperOptions wrapperOptio if ( value == null ) { return null; } + if ( ZoneOffset.class.isAssignableFrom( type ) ) { + return (X) value; + } if ( String.class.isAssignableFrom( type ) ) { return (X) toString( value ); } @@ -67,6 +70,9 @@ public ZoneOffset wrap(X value, WrapperOptions wrapperOptions) { if ( value == null ) { return null; } + if ( value instanceof ZoneOffset ) { + return (ZoneOffset) value; + } if ( value instanceof CharSequence ) { return fromString( (CharSequence) value ); } From 69aee622316f879a3c7dcbda07dd51e2a58317e6 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 27 Feb 2024 11:54:45 +0100 Subject: [PATCH 170/321] HHH-17769 Add test for issue --- .../criteria/CriteriaCteOffsetFetchTest.java | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaCteOffsetFetchTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaCteOffsetFetchTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaCteOffsetFetchTest.java new file mode 100644 index 000000000000..d01362ad9085 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/CriteriaCteOffsetFetchTest.java @@ -0,0 +1,173 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.criteria; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.query.criteria.HibernateCriteriaBuilder; +import org.hibernate.query.criteria.JpaCriteriaQuery; +import org.hibernate.query.criteria.JpaCteCriteria; +import org.hibernate.query.criteria.JpaRoot; +import org.hibernate.query.spi.QueryImplementor; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Tuple; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Root; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Marco Belladelli + */ +@DomainModel( annotatedClasses = CriteriaCteOffsetFetchTest.Product.class ) +@SessionFactory +@Jira( "https://hibernate.atlassian.net/browse/HHH-17769" ) +public class CriteriaCteOffsetFetchTest { + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.persist( new Product( "product_1", 1d ) ); + session.persist( new Product( "product_2", 100d ) ); + session.persist( new Product( "product_3", 200d ) ); + session.persist( new Product( "product_4", 30d ) ); + session.persist( new Product( "product_5", 20d ) ); + } ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> session.createMutationQuery( "delete from Product" ).executeUpdate() ); + } + + @Test + public void testMainQueryOffset(SessionFactoryScope scope) { + executeQuery( scope, 1, null, false ); + } + + @Test + public void testSetFirstResult(SessionFactoryScope scope) { + executeQuery( scope, 1, null, true ); + } + + @Test + public void testMainQueryFetch(SessionFactoryScope scope) { + executeQuery( scope, null, 2, false ); + } + + @Test + public void testSetMaxResults(SessionFactoryScope scope) { + executeQuery( scope, null, 2, true ); + } + + @Test + public void testMainQueryOffsetAndFetch(SessionFactoryScope scope) { + executeQuery( scope, 1, 2, false ); + } + + @Test + public void testSetFirstAndMaxResults(SessionFactoryScope scope) { + executeQuery( scope, 1, 2, true ); + } + + private void executeQuery( + SessionFactoryScope scope, + Integer firstResult, + Integer maxResults, + boolean queryOptions) { + scope.inTransaction( session -> { + final HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); + final JpaCriteriaQuery cq = cb.createQuery( Product.class ); + final JpaRoot root = cq.from( Product.class ); + + final JpaCteCriteria first = cq.with( cteQuery( cb, "id_for_first", 1.2d ) ); + final JpaRoot fromFirst = cq.from( first ); + final JpaCteCriteria second = cq.with( cteQuery( cb, "id_for_second", 150.0d ) ); + final JpaRoot fromSecond = cq.from( second ); + + cq.select( root ).where( cb.and( + cb.equal( root.get( "id" ), fromFirst.get( "id_for_first" ) ), + cb.equal( root.get( "id" ), fromSecond.get( "id_for_second" ) ) + ) ).orderBy( cb.asc( root.get( "name" ) ) ); + if ( !queryOptions ) { + if ( firstResult != null ) { + cq.offset( firstResult ); + } + if ( maxResults != null ) { + cq.fetch( maxResults ); + } + } + + final QueryImplementor query = session.createQuery( cq ); + if ( queryOptions ) { + if ( firstResult != null ) { + query.setFirstResult( firstResult ); + } + if ( maxResults != null ) { + query.setMaxResults( maxResults ); + } + } + final List resultList = query.getResultList(); + assertThat( resultList ).hasSize( 2 ); + final List names = new ArrayList<>( 3 ); + names.add( firstResult == null ? "product_2" : "product_5" ); + names.add( "product_4" ); + names.sort( String::compareTo ); + assertThat( resultList.stream().map( Product::getName ) ).containsExactly( names.toArray( new String[0] ) ); + } ); + } + + private CriteriaQuery cteQuery(CriteriaBuilder cb, String idAlias, double param) { + final CriteriaQuery cq = cb.createTupleQuery(); + final Root root = cq.from( Product.class ); + final Path price = root.get( "price" ); + return cq.multiselect( root.get( "id" ).alias( idAlias ) ).where( cb.and( + cb.isNotNull( price ), + param < 100 ? cb.greaterThan( price, param ) : cb.lessThan( price, param ) + ) ); + } + + @Entity( name = "Product" ) + public static class Product { + @Id + @GeneratedValue + private Long id; + + private String name; + + private Double price; + + public Product() { + } + + public Product(String name, Double price) { + this.name = name; + this.price = price; + } + + public String getName() { + return name; + } + + public Double getPrice() { + return price; + } + } +} From 38ad704ea20225bf5d8ad3e3e57659ecac38b6e2 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 27 Feb 2024 11:58:41 +0100 Subject: [PATCH 171/321] HHH-17769 Ignore query options limit when rendering cte definitions --- .../org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index bc907988be13..6ea4be24aabb 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -1930,8 +1930,11 @@ private boolean isInSubquery() { protected void visitCteDefinition(CteStatement cte) { final CteStatement oldCteStatement = currentCteStatement; currentCteStatement = cte; + final Limit oldLimit = limit; + limit = null; cte.getCteDefinition().accept( this ); currentCteStatement = oldCteStatement; + limit = oldLimit; } /** @@ -5571,11 +5574,14 @@ protected void inlineCteTableGroup(TableGroup tableGroup, LockMode lockMode) { true, null ); + final Limit oldLimit = limit; + limit = null; statementStack.push( cteDefinition ); renderPrimaryTableReference( queryPartTableGroup, lockMode ); if ( queryPartTableGroup.isLateral() && !dialect.supportsLateral() ) { addAdditionalWherePredicate( determineLateralEmulationPredicate( queryPartTableGroup ) ); } + limit = oldLimit; statementStack.pop(); } From 89dfa61b0c3d09fd13b6911040b9c67dbff43351 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Mon, 26 Feb 2024 12:43:38 +0100 Subject: [PATCH 172/321] HHH-17763 Add test for issue --- .../SchemaFilterProviderTest.java | 235 ++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/schemafilter/SchemaFilterProviderTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemafilter/SchemaFilterProviderTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/schemafilter/SchemaFilterProviderTest.java new file mode 100644 index 000000000000..9a7278f5bc77 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/schemafilter/SchemaFilterProviderTest.java @@ -0,0 +1,235 @@ +package org.hibernate.orm.test.schemafilter; + +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; + +import org.hibernate.boot.Metadata; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.model.relational.Namespace; +import org.hibernate.boot.model.relational.Sequence; +import org.hibernate.boot.registry.internal.StandardServiceRegistryImpl; +import org.hibernate.cfg.Environment; +import org.hibernate.cfg.SchemaToolingSettings; +import org.hibernate.internal.util.PropertiesHelper; +import org.hibernate.mapping.Table; +import org.hibernate.tool.schema.internal.ExceptionHandlerHaltImpl; +import org.hibernate.tool.schema.internal.Helper; +import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool; +import org.hibernate.tool.schema.internal.SchemaCreatorImpl; +import org.hibernate.tool.schema.spi.ContributableMatcher; +import org.hibernate.tool.schema.spi.ExceptionHandler; +import org.hibernate.tool.schema.spi.ExecutionOptions; +import org.hibernate.tool.schema.spi.SchemaFilter; +import org.hibernate.tool.schema.spi.SchemaFilterProvider; + +import org.hibernate.testing.ServiceRegistryBuilder; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.transaction.TransactionUtil; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * @author Marco Belladelli + */ +@TestInstance( TestInstance.Lifecycle.PER_CLASS ) +@Jira( "https://hibernate.atlassian.net/browse/HHH-17763" ) +public class SchemaFilterProviderTest { + @Test + public void testValidationDefaultProvider() { + testSchemaValidation( false ); + } + + @Test + public void testValidationCustomProvider() { + testSchemaValidation( true ); + } + + private void testSchemaValidation(boolean useCustomFilterProvider) { + final Map options = getFilterProviderConfig( useCustomFilterProvider ); + withServiceRegistry( options, (serviceRegistry, metadata) -> { + try { + TransactionUtil.doWithJDBC( serviceRegistry, connection -> { + try (final Statement statement = connection.createStatement()) { + statement.executeUpdate( "create table entity_1(id integer not null, primary key (id))" ); + // create entity_2 table with wrong column type for `name` + statement.executeUpdate( + "create table entity_2(id integer not null, name integer, primary key (id))" + ); + } + } ); + + final HibernateSchemaManagementTool tool = new HibernateSchemaManagementTool(); + tool.injectServices( serviceRegistry ); + try { + tool.getSchemaValidator( options ).doValidation( metadata, new ExecutionOptions() { + @Override + public boolean shouldManageNamespaces() { + return Helper.interpretNamespaceHandling( options ); + } + + @Override + public Map getConfigurationValues() { + return options; + } + + @Override + public ExceptionHandler getExceptionHandler() { + return ExceptionHandlerHaltImpl.INSTANCE; + } + }, ContributableMatcher.ALL ); + if ( !useCustomFilterProvider ) { + fail( "Expected schema validation to fail on Entity2#name field" ); + } + } + catch (Exception e) { + if ( useCustomFilterProvider ) { + fail( "Unexpected exception when creating session factory", e ); + } + else { + assertThat( e ).hasMessageContaining( + "wrong column type encountered in column [name] in table [entity_2]" + ); + } + } + + TransactionUtil.doWithJDBC( serviceRegistry, connection -> { + try (final Statement statement = connection.createStatement()) { + statement.executeUpdate( "drop table entity_1" ); + statement.executeUpdate( "drop table entity_2" ); + } + } ); + } + catch (SQLException e) { + throw new RuntimeException( e ); + } + } ); + } + + @Test + public void testCreationDefaultFilter() { + testSchemaCreation( false ); + } + + @Test + public void testCreationCustomFilter() { + testSchemaCreation( true ); + } + + private void testSchemaCreation(boolean useCustomFilterProvider) { + final Map options = getFilterProviderConfig( useCustomFilterProvider ); + withServiceRegistry( options, (serviceRegistry, metadata) -> { + final HibernateSchemaManagementTool tool = new HibernateSchemaManagementTool(); + tool.injectServices( serviceRegistry ); + final SchemaCreatorImpl schemaCreator = (SchemaCreatorImpl) tool.getSchemaCreator( options ); + final List commands = schemaCreator.generateCreationCommands( + metadata, + false + ); + assertThat( commands ).hasSize( useCustomFilterProvider ? 1 : 2 ); + assertThat( commands ).anyMatch( s -> s.startsWith( "create table entity_1" ) ); + if ( useCustomFilterProvider ) { + assertThat( commands ).noneMatch( s -> s.startsWith( "create table entity_2" ) ); + } + else { + assertThat( commands ).anyMatch( s -> s.startsWith( "create table entity_2" ) ); + } + } ); + } + + private void withServiceRegistry( + Map configurationValues, + BiConsumer consumer) { + final Map environmentProperties = PropertiesHelper.map( Environment.getProperties() ); + final Map settings = new HashMap<>( environmentProperties.size() + configurationValues.size() ); + settings.putAll( environmentProperties ); + settings.putAll( configurationValues ); + try (final StandardServiceRegistryImpl serviceRegistry = ServiceRegistryBuilder.buildServiceRegistry( settings )) { + consumer.accept( serviceRegistry, new MetadataSources( serviceRegistry ).addAnnotatedClasses( + Entity1.class, + Entity2.class + ).buildMetadata() ); + } + } + + private Map getFilterProviderConfig(boolean useCustomFilterProvider) { + return useCustomFilterProvider + ? Map.of( SchemaToolingSettings.HBM2DDL_FILTER_PROVIDER, CustomFilterProvider.INSTANCE ) + : Map.of(); + } + + @Entity( name = "Entity1" ) + @jakarta.persistence.Table( name = "entity_1" ) + public static class Entity1 { + @Id + private Integer id; + } + + @Entity( name = "Entity2" ) + @jakarta.persistence.Table( name = "entity_2" ) + public static class Entity2 { + @Id + private Integer id; + + @Column( length = 255 ) + private String name; + } + + public static class CustomFilterProvider implements SchemaFilterProvider { + public static final CustomFilterProvider INSTANCE = new CustomFilterProvider(); + + @Override + public SchemaFilter getCreateFilter() { + return CustomSchemaFilter.INSTANCE; + } + + @Override + public SchemaFilter getDropFilter() { + return CustomSchemaFilter.INSTANCE; + } + + @Override + public SchemaFilter getTruncatorFilter() { + return CustomSchemaFilter.INSTANCE; + } + + @Override + public SchemaFilter getMigrateFilter() { + return CustomSchemaFilter.INSTANCE; + } + + @Override + public SchemaFilter getValidateFilter() { + return CustomSchemaFilter.INSTANCE; + } + } + + public static class CustomSchemaFilter implements SchemaFilter { + public static CustomSchemaFilter INSTANCE = new CustomSchemaFilter(); + + @Override + public boolean includeNamespace(Namespace namespace) { + return true; + } + + @Override + public boolean includeTable(Table table) { + return table.getName().equals( "entity_1" ); + } + + @Override + public boolean includeSequence(Sequence sequence) { + return true; + } + } +} From c956208926c9099c7fd8ebfb35b61996cad237a4 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Mon, 26 Feb 2024 12:44:34 +0100 Subject: [PATCH 173/321] HHH-17763 Rely on action-specific `SchemaFilter` instances Deprecated `ExecutionOptions#getSchemaFilter` which was always returning the default implementation (not filtering anything) --- .../internal/AbstractSchemaMigrator.java | 6 +-- .../internal/AbstractSchemaValidator.java | 6 +-- .../internal/GroupedSchemaMigratorImpl.java | 6 +-- .../internal/GroupedSchemaValidatorImpl.java | 2 +- .../IndividuallySchemaMigratorImpl.java | 6 +-- .../IndividuallySchemaValidatorImpl.java | 2 +- .../schema/internal/SchemaCreatorImpl.java | 45 ++++++++++--------- .../schema/internal/SchemaDropperImpl.java | 29 ++++++------ .../schema/internal/SchemaTruncatorImpl.java | 19 +++++--- .../tool/schema/spi/ExecutionOptions.java | 8 +++- .../spi/SchemaManagementToolCoordinator.java | 28 +++++------- 11 files changed, 82 insertions(+), 75 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaMigrator.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaMigrator.java index f21945b8af1c..4b9b5976338e 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaMigrator.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaMigrator.java @@ -244,7 +244,7 @@ private void performMigration( sqlGenerationContext, targets ); tablesInformation.put( namespace, nameSpaceTablesInformation ); - if ( options.getSchemaFilter().includeNamespace( namespace ) ) { + if ( schemaFilter.includeNamespace( namespace ) ) { for ( Sequence sequence : namespace.getSequences() ) { if ( contributableInclusionFilter.matches( sequence ) ) { checkExportIdentifier( sequence, exportIdentifiers); @@ -259,10 +259,10 @@ private void performMigration( //NOTE : Foreign keys must be created *after* all tables of all namespaces for cross namespace fks. see HHH-10420 for ( Namespace namespace : database.getNamespaces() ) { - if ( options.getSchemaFilter().includeNamespace( namespace ) ) { + if ( schemaFilter.includeNamespace( namespace ) ) { final NameSpaceTablesInformation nameSpaceTablesInformation = tablesInformation.get( namespace ); for ( Table table : namespace.getTables() ) { - if ( options.getSchemaFilter().includeTable( table ) && contributableInclusionFilter.matches( table ) ) { + if ( schemaFilter.includeTable( table ) && contributableInclusionFilter.matches( table ) ) { final TableInformation tableInformation = nameSpaceTablesInformation.getTableInformation( table ); if ( tableInformation == null || tableInformation.isPhysicalTable() ) { applyForeignKeys( table, tableInformation, dialect, metadata, formatter, options, diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java index b88967b16357..18e95aa9d863 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java @@ -93,15 +93,15 @@ public void performValidation( ContributableMatcher contributableInclusionFilter, Dialect dialect) { for ( Namespace namespace : metadata.getDatabase().getNamespaces() ) { - if ( options.getSchemaFilter().includeNamespace( namespace ) ) { + if ( schemaFilter.includeNamespace( namespace ) ) { validateTables( metadata, databaseInformation, options, contributableInclusionFilter, dialect, namespace ); } } for ( Namespace namespace : metadata.getDatabase().getNamespaces() ) { - if ( options.getSchemaFilter().includeNamespace( namespace ) ) { + if ( schemaFilter.includeNamespace( namespace ) ) { for ( Sequence sequence : namespace.getSequences() ) { - if ( ! options.getSchemaFilter().includeSequence( sequence ) ) { + if ( !schemaFilter.includeSequence( sequence ) ) { continue; } diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/GroupedSchemaMigratorImpl.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/GroupedSchemaMigratorImpl.java index 9c4ff8131b91..e5fe09cd9837 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/GroupedSchemaMigratorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/GroupedSchemaMigratorImpl.java @@ -55,7 +55,7 @@ protected NameSpaceTablesInformation performTablesMigration( final NameSpaceTablesInformation tablesInformation = new NameSpaceTablesInformation( metadata.getDatabase().getJdbcEnvironment().getIdentifierHelper() ); - if ( options.getSchemaFilter().includeNamespace( namespace ) ) { + if ( schemaFilter.includeNamespace( namespace ) ) { createSchemaAndCatalog( existingDatabase, options, @@ -71,7 +71,7 @@ protected NameSpaceTablesInformation performTablesMigration( final NameSpaceTablesInformation tables = existingDatabase.getTablesInformation( namespace ); for ( Table table : namespace.getTables() ) { - if ( options.getSchemaFilter().includeTable( table ) + if ( schemaFilter.includeTable( table ) && table.isPhysicalTable() && contributableInclusionFilter.matches( table ) ) { checkExportIdentifier( table, exportIdentifiers ); @@ -88,7 +88,7 @@ else if ( tableInformation.isPhysicalTable() ) { } for ( Table table : namespace.getTables() ) { - if ( options.getSchemaFilter().includeTable( table ) + if ( schemaFilter.includeTable( table ) && table.isPhysicalTable() && contributableInclusionFilter.matches( table ) ) { final TableInformation tableInformation = tablesInformation.getTableInformation( table ); diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/GroupedSchemaValidatorImpl.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/GroupedSchemaValidatorImpl.java index 2716c1780e2d..9240ecf49ecd 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/GroupedSchemaValidatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/GroupedSchemaValidatorImpl.java @@ -40,7 +40,7 @@ protected void validateTables( final NameSpaceTablesInformation tables = databaseInformation.getTablesInformation( namespace ); for ( Table table : namespace.getTables() ) { - if ( options.getSchemaFilter().includeTable( table ) + if ( schemaFilter.includeTable( table ) && table.isPhysicalTable() && contributableInclusionFilter.matches( table ) ) { validateTable( diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/IndividuallySchemaMigratorImpl.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/IndividuallySchemaMigratorImpl.java index 68e8611b105c..cab1bf96a4bb 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/IndividuallySchemaMigratorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/IndividuallySchemaMigratorImpl.java @@ -55,7 +55,7 @@ protected NameSpaceTablesInformation performTablesMigration( final NameSpaceTablesInformation tablesInformation = new NameSpaceTablesInformation( metadata.getDatabase().getJdbcEnvironment().getIdentifierHelper() ); - if ( options.getSchemaFilter().includeNamespace( namespace ) ) { + if ( schemaFilter.includeNamespace( namespace ) ) { createSchemaAndCatalog( existingDatabase, options, @@ -69,7 +69,7 @@ protected NameSpaceTablesInformation performTablesMigration( targets ); for ( Table table : namespace.getTables() ) { - if ( options.getSchemaFilter().includeTable( table ) + if ( schemaFilter.includeTable( table ) && table.isPhysicalTable() && contributableInclusionFilter.matches( table ) ) { checkExportIdentifier( table, exportIdentifiers ); @@ -86,7 +86,7 @@ else if ( tableInformation.isPhysicalTable() ) { } for ( Table table : namespace.getTables() ) { - if ( options.getSchemaFilter().includeTable( table ) + if ( schemaFilter.includeTable( table ) && table.isPhysicalTable() && contributableInclusionFilter.matches( table ) ) { final TableInformation tableInformation = tablesInformation.getTableInformation( table ); diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/IndividuallySchemaValidatorImpl.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/IndividuallySchemaValidatorImpl.java index b7be9d2afb02..0ef9209eed3b 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/IndividuallySchemaValidatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/IndividuallySchemaValidatorImpl.java @@ -39,7 +39,7 @@ protected void validateTables( Dialect dialect, Namespace namespace) { for ( Table table : namespace.getTables() ) { - if ( options.getSchemaFilter().includeTable( table ) + if ( schemaFilter.includeTable( table ) && table.isPhysicalTable() && contributableInclusionFilter.matches( table ) ) { final TableInformation tableInformation = databaseInformation.getTableInformation( diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaCreatorImpl.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaCreatorImpl.java index 01f1d7b9a754..730041d02522 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaCreatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaCreatorImpl.java @@ -230,15 +230,16 @@ public void createFromMetadata( final SqlStringGenerationContext context = createSqlStringGenerationContext(options, metadata); final Set exportIdentifiers = setOfSize(50); - createSchemasAndCatalogs(metadata, options, dialect, formatter, context, targets); + createSchemasAndCatalogs(metadata, options, schemaFilter, dialect, formatter, context, targets); // next, create all "before table" auxiliary objects createAuxiliaryObjectsBeforeTables(metadata, options, dialect, formatter, context, exportIdentifiers, targets); // next, create all UDTs - createUserDefinedTypes(metadata, options, dialect, formatter, context, targets); + createUserDefinedTypes(metadata, options, schemaFilter, dialect, formatter, context, targets); // then, create all schema objects (tables, sequences, constraints, etc) in each schema createSequencesTablesConstraints( metadata, options, + schemaFilter, contributableInclusionMatcher, dialect, formatter, @@ -247,7 +248,7 @@ public void createFromMetadata( targets ); // foreign keys must be created after all tables of all namespaces for cross-namespace constraints (see HHH-10420) - createForeignKeys( metadata, options, contributableInclusionMatcher, dialect, formatter, context, targets ); + createForeignKeys( metadata, options, schemaFilter, contributableInclusionMatcher, dialect, formatter, context, targets ); // next, create all "after table" auxiliary objects createAuxiliaryObjectsAfterTables( metadata, options, dialect, formatter, context, exportIdentifiers, targets ); // and finally add all init commands @@ -287,6 +288,7 @@ private static void createAuxiliaryObjectsAfterTables( private static void createForeignKeys( Metadata metadata, ExecutionOptions options, + SchemaFilter schemaFilter, ContributableMatcher contributableInclusionMatcher, Dialect dialect, Formatter formatter, @@ -294,9 +296,9 @@ private static void createForeignKeys( GenerationTarget[] targets) { for ( Namespace namespace : metadata.getDatabase().getNamespaces() ) { // foreign keys must be created after unique keys for numerous DBs (see HHH-8390) - if ( options.getSchemaFilter().includeNamespace( namespace ) ) { + if ( schemaFilter.includeNamespace( namespace ) ) { for ( Table table : namespace.getTables() ) { - if ( options.getSchemaFilter().includeTable( table ) + if ( schemaFilter.includeTable( table ) && contributableInclusionMatcher.matches( table ) ) { // foreign keys for ( ForeignKey foreignKey : table.getForeignKeys().values() ) { @@ -316,6 +318,7 @@ private static void createForeignKeys( private static void createSequencesTablesConstraints( Metadata metadata, ExecutionOptions options, + SchemaFilter schemaFilter, ContributableMatcher contributableInclusionMatcher, Dialect dialect, Formatter formatter, @@ -323,11 +326,12 @@ private static void createSequencesTablesConstraints( Set exportIdentifiers, GenerationTarget[] targets) { for ( Namespace namespace : metadata.getDatabase().getNamespaces() ) { - if ( options.getSchemaFilter().includeNamespace( namespace ) ) { + if ( schemaFilter.includeNamespace( namespace ) ) { // sequences createSequences( metadata, options, + schemaFilter, contributableInclusionMatcher, dialect, formatter, @@ -340,6 +344,7 @@ private static void createSequencesTablesConstraints( createTables( metadata, options, + schemaFilter, contributableInclusionMatcher, dialect, formatter, @@ -351,6 +356,7 @@ private static void createSequencesTablesConstraints( createTableConstraints( metadata, options, + schemaFilter, contributableInclusionMatcher, dialect, formatter, @@ -366,6 +372,7 @@ private static void createSequencesTablesConstraints( private static void createTableConstraints( Metadata metadata, ExecutionOptions options, + SchemaFilter schemaFilter, ContributableMatcher contributableInclusionMatcher, Dialect dialect, Formatter formatter, @@ -375,7 +382,7 @@ private static void createTableConstraints( Namespace namespace) { for ( Table table : namespace.getTables() ) { if ( table.isPhysicalTable() - && options.getSchemaFilter().includeTable( table ) + && schemaFilter.includeTable( table ) && contributableInclusionMatcher.matches( table ) ) { // indexes for ( Index index : table.getIndexes().values() ) { @@ -404,6 +411,7 @@ private static void createTableConstraints( private static void createTables( Metadata metadata, ExecutionOptions options, + SchemaFilter schemaFilter, ContributableMatcher contributableInclusionMatcher, Dialect dialect, Formatter formatter, @@ -414,7 +422,7 @@ private static void createTables( for ( Table table : namespace.getTables() ) { if ( table.isPhysicalTable() && !table.isView() - && options.getSchemaFilter().includeTable( table ) + && schemaFilter.includeTable( table ) && contributableInclusionMatcher.matches( table ) ) { checkExportIdentifier( table, exportIdentifiers ); applySqlStrings( @@ -428,7 +436,7 @@ private static void createTables( for ( Table table : namespace.getTables() ) { if ( table.isPhysicalTable() && table.isView() - && options.getSchemaFilter().includeTable( table ) + && schemaFilter.includeTable( table ) && contributableInclusionMatcher.matches( table ) ) { checkExportIdentifier( table, exportIdentifiers ); applySqlStrings( @@ -444,6 +452,7 @@ private static void createTables( private static void createSequences( Metadata metadata, ExecutionOptions options, + SchemaFilter schemaFilter, ContributableMatcher contributableInclusionMatcher, Dialect dialect, Formatter formatter, @@ -452,7 +461,7 @@ private static void createSequences( GenerationTarget[] targets, Namespace namespace) { for ( Sequence sequence : namespace.getSequences() ) { - if ( options.getSchemaFilter().includeSequence( sequence ) + if ( schemaFilter.includeSequence( sequence ) && contributableInclusionMatcher.matches( sequence ) ) { checkExportIdentifier( sequence, exportIdentifiers); applySqlStrings( @@ -491,12 +500,13 @@ private static void createAuxiliaryObjectsBeforeTables( private static void createUserDefinedTypes( Metadata metadata, ExecutionOptions options, + SchemaFilter schemaFilter, Dialect dialect, Formatter formatter, SqlStringGenerationContext context, GenerationTarget[] targets) { for ( Namespace namespace : metadata.getDatabase().getNamespaces() ) { - if ( options.getSchemaFilter().includeNamespace( namespace ) ) { + if ( schemaFilter.includeNamespace( namespace ) ) { for ( UserDefinedType userDefinedType : namespace.getDependencyOrderedUserDefinedTypes() ) { applySqlStrings( dialect.getUserDefinedTypeExporter() @@ -513,6 +523,7 @@ private static void createUserDefinedTypes( private static void createSchemasAndCatalogs( Metadata metadata, ExecutionOptions options, + SchemaFilter schemaFilter, Dialect dialect, Formatter formatter, SqlStringGenerationContext context, @@ -523,7 +534,7 @@ private static void createSchemasAndCatalogs( if ( tryToCreateCatalogs || tryToCreateSchemas ) { Set exportedCatalogs = new HashSet<>(); for ( Namespace namespace : metadata.getDatabase().getNamespaces() ) { - if ( options.getSchemaFilter().includeNamespace( namespace ) ) { + if ( schemaFilter.includeNamespace( namespace ) ) { Namespace.Name logicalName = namespace.getName(); Namespace.Name physicalName = namespace.getPhysicalName(); @@ -738,11 +749,6 @@ public Map getConfigurationValues() { public ExceptionHandler getExceptionHandler() { return ExceptionHandlerHaltImpl.INSTANCE; } - - @Override - public SchemaFilter getSchemaFilter() { - return schemaFilter; - } }; createFromMetadata( metadata, options, dialect, FormatStyle.NONE.getFormatter(), target ); @@ -798,11 +804,6 @@ public Map getConfigurationValues() { public ExceptionHandler getExceptionHandler() { return ExceptionHandlerLoggedImpl.INSTANCE; } - - @Override - public SchemaFilter getSchemaFilter() { - return schemaFilter; - } }, (contributed) -> true, new SourceDescriptor() { diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaDropperImpl.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaDropperImpl.java index 76e6eeb81446..d1b1ecdeca39 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaDropperImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaDropperImpl.java @@ -222,8 +222,8 @@ private void dropFromMetadata( targets ); dropAuxiliaryObjectsAfterTables( metadata, options, dialect, formatter, context, targets ); - dropUserDefinedTypes( metadata, options, dialect, formatter, context, targets ); - dropSchemasAndCatalogs( metadata, options, dialect, formatter, context, targets ); + dropUserDefinedTypes( metadata, options, schemaFilter, dialect, formatter, context, targets ); + dropSchemasAndCatalogs( metadata, options, schemaFilter, dialect, formatter, context, targets ); } private void dropConstraintsTablesSequences( @@ -236,7 +236,7 @@ private void dropConstraintsTablesSequences( GenerationTarget[] targets) { final Set exportIdentifiers = setOfSize( 50 ); for ( Namespace namespace : metadata.getDatabase().getNamespaces() ) { - if ( options.getSchemaFilter().includeNamespace( namespace ) ) { + if ( schemaFilter.includeNamespace( namespace ) ) { // we need to drop all constraints/indexes prior to dropping the tables applyConstraintDropping( @@ -253,6 +253,7 @@ private void dropConstraintsTablesSequences( dropTables( metadata, options, + schemaFilter, inclusionFilter, dialect, formatter, @@ -265,6 +266,7 @@ private void dropConstraintsTablesSequences( dropSequences( metadata, options, + schemaFilter, inclusionFilter, dialect, formatter, @@ -323,6 +325,7 @@ private static void dropAuxiliaryObjectsAfterTables( private static void dropSequences( Metadata metadata, ExecutionOptions options, + SchemaFilter schemaFilter, ContributableMatcher inclusionFilter, Dialect dialect, Formatter formatter, @@ -331,7 +334,7 @@ private static void dropSequences( Namespace namespace, GenerationTarget[] targets) { for ( Sequence sequence : namespace.getSequences() ) { - if ( options.getSchemaFilter().includeSequence( sequence ) + if ( schemaFilter.includeSequence( sequence ) && inclusionFilter.matches( sequence ) ) { checkExportIdentifier( sequence, exportIdentifiers); applySqlStrings( @@ -347,6 +350,7 @@ private static void dropSequences( private static void dropTables( Metadata metadata, ExecutionOptions options, + SchemaFilter schemaFilter, ContributableMatcher inclusionFilter, Dialect dialect, Formatter formatter, @@ -357,7 +361,7 @@ private static void dropTables( for ( Table table : namespace.getTables() ) { if ( table.isPhysicalTable() && table.isView() - && options.getSchemaFilter().includeTable( table ) + && schemaFilter.includeTable( table ) && inclusionFilter.matches( table ) ) { checkExportIdentifier( table, exportIdentifiers); applySqlStrings( @@ -371,7 +375,7 @@ private static void dropTables( for ( Table table : namespace.getTables() ) { if ( table.isPhysicalTable() && !table.isView() - && options.getSchemaFilter().includeTable( table ) + && schemaFilter.includeTable( table ) && inclusionFilter.matches( table ) ) { checkExportIdentifier( table, exportIdentifiers); applySqlStrings( @@ -387,12 +391,13 @@ private static void dropTables( private static void dropUserDefinedTypes( Metadata metadata, ExecutionOptions options, + SchemaFilter schemaFilter, Dialect dialect, Formatter formatter, SqlStringGenerationContext context, GenerationTarget[] targets) { for ( Namespace namespace : metadata.getDatabase().getNamespaces() ) { - if ( options.getSchemaFilter().includeNamespace( namespace ) ) { + if ( schemaFilter.includeNamespace( namespace ) ) { final List dependencyOrderedUserDefinedTypes = namespace.getDependencyOrderedUserDefinedTypes(); Collections.reverse( dependencyOrderedUserDefinedTypes ); for ( UserDefinedType userDefinedType : dependencyOrderedUserDefinedTypes ) { @@ -411,6 +416,7 @@ private static void dropUserDefinedTypes( private static void dropSchemasAndCatalogs( Metadata metadata, ExecutionOptions options, + SchemaFilter schemaFilter, Dialect dialect, Formatter formatter, SqlStringGenerationContext context, @@ -420,7 +426,7 @@ private static void dropSchemasAndCatalogs( if ( tryToDropCatalogs || tryToDropSchemas) { final Set exportedCatalogs = new HashSet<>(); for ( Namespace namespace : metadata.getDatabase().getNamespaces() ) { - if ( options.getSchemaFilter().includeNamespace( namespace ) ) { + if ( schemaFilter.includeNamespace( namespace ) ) { Namespace.Name logicalName = namespace.getName(); Namespace.Name physicalName = namespace.getPhysicalName(); @@ -464,7 +470,7 @@ private void applyConstraintDropping( if ( dialect.dropConstraints() ) { for ( Table table : namespace.getTables() ) { if ( table.isPhysicalTable() - && options.getSchemaFilter().includeTable( table ) + && schemaFilter.includeTable( table ) && inclusionFilter.matches( table ) ) { for ( ForeignKey foreignKey : table.getForeignKeys().values() ) { applySqlStrings( @@ -552,11 +558,6 @@ public Map getConfigurationValues() { public ExceptionHandler getExceptionHandler() { return ExceptionHandlerLoggedImpl.INSTANCE; } - - @Override - public SchemaFilter getSchemaFilter() { - return schemaFilter; - } }, (contributed) -> true, serviceRegistry.getService( JdbcEnvironment.class ).getDialect(), diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaTruncatorImpl.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaTruncatorImpl.java index d41e2345bb7e..5a2bfc9da316 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaTruncatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaTruncatorImpl.java @@ -58,9 +58,11 @@ public class SchemaTruncatorImpl implements SchemaTruncator { private static final Logger log = Logger.getLogger( SchemaTruncatorImpl.class ); private final HibernateSchemaManagementTool tool; + private final SchemaFilter schemaFilter; public SchemaTruncatorImpl(HibernateSchemaManagementTool tool, SchemaFilter truncatorFilter) { this.tool = tool; + schemaFilter = truncatorFilter; } @Override @@ -111,12 +113,13 @@ private void performTruncate( final boolean format = Helper.interpretFormattingEnabled( options.getConfigurationValues() ); final Formatter formatter = format ? FormatStyle.DDL.getFormatter() : FormatStyle.NONE.getFormatter(); - truncateFromMetadata( metadata, options, contributableInclusionFilter, dialect, formatter, targets ); + truncateFromMetadata( metadata, options, schemaFilter, contributableInclusionFilter, dialect, formatter, targets ); } private void truncateFromMetadata( Metadata metadata, ExecutionOptions options, + SchemaFilter schemaFilter, ContributableMatcher contributableInclusionFilter, Dialect dialect, Formatter formatter, @@ -128,11 +131,11 @@ private void truncateFromMetadata( for ( Namespace namespace : database.getNamespaces() ) { - if ( ! options.getSchemaFilter().includeNamespace( namespace ) ) { + if ( ! schemaFilter.includeNamespace( namespace ) ) { continue; } - disableConstraints( namespace, metadata, formatter, options, context, + disableConstraints( namespace, metadata, formatter, options, schemaFilter, context, contributableInclusionFilter, targets ); applySqlString( dialect.getTableCleaner().getSqlBeforeString(), formatter, options,targets ); @@ -142,7 +145,7 @@ private void truncateFromMetadata( if ( ! table.isPhysicalTable() ) { continue; } - if ( ! options.getSchemaFilter().includeTable( table ) ) { + if ( ! schemaFilter.includeTable( table ) ) { continue; } if ( ! contributableInclusionFilter.matches( table ) ) { @@ -172,7 +175,7 @@ private void truncateFromMetadata( // } applySqlString( dialect.getTableCleaner().getSqlAfterString(), formatter, options,targets ); - enableConstraints( namespace, metadata, formatter, options, context, + enableConstraints( namespace, metadata, formatter, options, schemaFilter, context, contributableInclusionFilter, targets ); } @@ -186,6 +189,7 @@ private void disableConstraints( Metadata metadata, Formatter formatter, ExecutionOptions options, + SchemaFilter schemaFilter, SqlStringGenerationContext context, ContributableMatcher contributableInclusionFilter, GenerationTarget... targets) { @@ -195,7 +199,7 @@ private void disableConstraints( if ( !table.isPhysicalTable() ) { continue; } - if ( ! options.getSchemaFilter().includeTable( table ) ) { + if ( ! schemaFilter.includeTable( table ) ) { continue; } if ( ! contributableInclusionFilter.matches( table ) ) { @@ -232,6 +236,7 @@ private void enableConstraints( Metadata metadata, Formatter formatter, ExecutionOptions options, + SchemaFilter schemaFilter, SqlStringGenerationContext context, ContributableMatcher contributableInclusionFilter, GenerationTarget... targets) { @@ -241,7 +246,7 @@ private void enableConstraints( if ( !table.isPhysicalTable() ) { continue; } - if ( ! options.getSchemaFilter().includeTable( table ) ) { + if ( ! schemaFilter.includeTable( table ) ) { continue; } if ( ! contributableInclusionFilter.matches( table ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/spi/ExecutionOptions.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/spi/ExecutionOptions.java index c96f7bafb21b..74a2b00b8657 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/spi/ExecutionOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/spi/ExecutionOptions.java @@ -24,5 +24,11 @@ public interface ExecutionOptions { ExceptionHandler getExceptionHandler(); - SchemaFilter getSchemaFilter(); + /** + * @deprecated No longer used, see {@link org.hibernate.cfg.SchemaToolingSettings#HBM2DDL_FILTER_PROVIDER} + */ + @Deprecated( forRemoval = true ) + default SchemaFilter getSchemaFilter() { + throw new UnsupportedOperationException(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/spi/SchemaManagementToolCoordinator.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/spi/SchemaManagementToolCoordinator.java index 4d67d64b98e0..fc2d3ee444e7 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/spi/SchemaManagementToolCoordinator.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/spi/SchemaManagementToolCoordinator.java @@ -23,7 +23,6 @@ import org.hibernate.tool.schema.Action; import org.hibernate.tool.schema.SourceType; import org.hibernate.tool.schema.TargetType; -import org.hibernate.tool.schema.internal.DefaultSchemaFilter; import org.hibernate.tool.schema.internal.ExceptionHandlerHaltImpl; import org.hibernate.tool.schema.internal.ExceptionHandlerLoggedImpl; import org.hibernate.tool.schema.internal.Helper; @@ -173,17 +172,6 @@ public static void process( public static ExecutionOptions buildExecutionOptions( final Map configurationValues, final ExceptionHandler exceptionHandler) { - return buildExecutionOptions( - configurationValues, - DefaultSchemaFilter.INSTANCE, - exceptionHandler - ); - } - - public static ExecutionOptions buildExecutionOptions( - final Map configurationValues, - final SchemaFilter schemaFilter, - final ExceptionHandler exceptionHandler) { return new ExecutionOptions() { @Override public boolean shouldManageNamespaces() { @@ -199,14 +187,20 @@ public Map getConfigurationValues() { public ExceptionHandler getExceptionHandler() { return exceptionHandler; } - - @Override - public SchemaFilter getSchemaFilter() { - return schemaFilter; - } }; } + /** + * @deprecated Use {@link #buildExecutionOptions(Map, ExceptionHandler)} instead. + */ + @Deprecated(forRemoval = true) + public static ExecutionOptions buildExecutionOptions( + final Map configurationValues, + final SchemaFilter schemaFilter, + final ExceptionHandler exceptionHandler) { + return buildExecutionOptions( configurationValues, exceptionHandler ); + } + private static void performDatabaseAction( final Action action, Metadata metadata, From 85a2752c1d14abff62a5ce742d97328e6b55b0b0 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Tue, 5 Mar 2024 16:00:53 +0100 Subject: [PATCH 174/321] HHH-17808 NPE when null vector is read with VectorJdbcType --- .../src/main/java/org/hibernate/vector/VectorJdbcType.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hibernate-vector/src/main/java/org/hibernate/vector/VectorJdbcType.java b/hibernate-vector/src/main/java/org/hibernate/vector/VectorJdbcType.java index 031ef391d8e6..4b3636c139ff 100644 --- a/hibernate-vector/src/main/java/org/hibernate/vector/VectorJdbcType.java +++ b/hibernate-vector/src/main/java/org/hibernate/vector/VectorJdbcType.java @@ -68,6 +68,9 @@ protected X doExtract(CallableStatement statement, String name, WrapperOptions o } private float[] getFloatArray(String string) { + if ( string == null ) { + return null; + } if ( string.length() == 2 ) { return EMPTY; } From dcf91aadcad55aec97fa5cf0a5a07e5ee8fa5b1b Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Wed, 28 Feb 2024 18:37:25 +0100 Subject: [PATCH 175/321] HHH-17776 - Add test and fix Signed-off-by: Jan Schatteman --- .../query/sqm/tree/select/SqmQuerySpec.java | 15 ++-- ...hereClauseTest.java => SubqueryTests.java} | 83 +++++++++++++++++-- 2 files changed, 82 insertions(+), 16 deletions(-) rename hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/subquery/{RootInSubqueryWhereClauseTest.java => SubqueryTests.java} (55%) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java index d1c30c1f4e57..6ad3b64c3dae 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java @@ -67,13 +67,13 @@ public class SqmQuerySpec extends SqmQueryPart public SqmQuerySpec(NodeBuilder nodeBuilder) { super( nodeBuilder ); + // Enforce non-nullness of the fromClause + this.fromClause = new SqmFromClause(); } public SqmQuerySpec(SqmQuerySpec original, SqmCopyContext context) { super( original, context ); - if ( original.fromClause != null ) { - this.fromClause = original.fromClause.copy( context ); - } + this.fromClause = original.fromClause.copy( context ); if ( original.selectClause != null ) { this.selectClause = original.selectClause.copy( context ); } @@ -99,9 +99,7 @@ public SqmQuerySpec copy(SqmCopyContext context) { return existing; } final SqmQuerySpec querySpec = context.registerCopy( this, new SqmQuerySpec<>( nodeBuilder() ) ); - if ( fromClause != null ) { - querySpec.fromClause = fromClause.copy( context ); - } + querySpec.fromClause = fromClause.copy( context ); if ( selectClause != null ) { querySpec.selectClause = selectClause.copy( context ); } @@ -148,7 +146,10 @@ public SqmFromClause getFromClause() { } public void setFromClause(SqmFromClause fromClause) { - this.fromClause = fromClause; + // Enforce non-nullness of the fromClause + if ( fromClause != null ) { + this.fromClause = fromClause; + } } public boolean producesUniqueResults() { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/subquery/RootInSubqueryWhereClauseTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/subquery/SubqueryTests.java similarity index 55% rename from hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/subquery/RootInSubqueryWhereClauseTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/subquery/SubqueryTests.java index 7f60a2c990d0..334ee9aa6126 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/subquery/RootInSubqueryWhereClauseTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/subquery/SubqueryTests.java @@ -9,35 +9,44 @@ import java.util.ArrayList; import java.util.List; -import org.hibernate.testing.TestForIssue; +import org.hibernate.Session; +import org.hibernate.query.criteria.HibernateCriteriaBuilder; +import org.hibernate.query.criteria.JpaCriteriaQuery; +import org.hibernate.query.criteria.JpaDerivedRoot; +import org.hibernate.query.criteria.JpaSubQuery; + import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.Jpa; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import jakarta.persistence.Entity; +import jakarta.persistence.EntityManager; import jakarta.persistence.Id; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; +import jakarta.persistence.Tuple; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Root; import jakarta.persistence.criteria.Subquery; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; @Jpa( annotatedClasses = { - RootInSubqueryWhereClauseTest.Person.class, - RootInSubqueryWhereClauseTest.Address.class + SubqueryTests.Person.class, + SubqueryTests.Address.class } ) -@TestForIssue(jiraKey = "HHH-15477") -public class RootInSubqueryWhereClauseTest { +public class SubqueryTests { - @BeforeEach + @BeforeAll public void setUp(EntityManagerFactoryScope scope){ scope.inTransaction( entityManager -> { @@ -54,7 +63,8 @@ public void setUp(EntityManagerFactoryScope scope){ } @Test - public void testSubquery(EntityManagerFactoryScope scope) { + @JiraKey(value = "HHH-15477") + public void testRootInSubqueryWhereClause(EntityManagerFactoryScope scope) { scope.inTransaction( entityManager -> { CriteriaBuilder builder = entityManager.getCriteriaBuilder(); @@ -76,6 +86,50 @@ public void testSubquery(EntityManagerFactoryScope scope) { ); } + @Test + @JiraKey(value = "HHH-17776") + public void testNoFromClauseInSubquery(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + List entities = getEntities(entityManager); + assertEquals( 2, entities.size()); + assertEquals("Jack", entities.get(0).getName()); + assertEquals("Black", entities.get(0).getSurName()); + assertEquals("John", entities.get(1).getName()); + assertEquals("Doe", entities.get(1).getSurName()); + } + ); + } + + private List getEntities(EntityManager entityManager) { + + HibernateCriteriaBuilder builder = entityManager.unwrap( Session.class ).getCriteriaBuilder(); + JpaCriteriaQuery mainQuery = builder.createQuery( Person.class ); + + JpaSubQuery q1 = mainQuery.subquery(Tuple.class); + q1.multiselect( + builder.literal("John").alias("name"), + builder.literal("Doe").alias("surName") + ); + + JpaSubQuery q2 = mainQuery.subquery(Tuple.class); + q2.multiselect( + builder.literal("Jack").alias("name"), + builder.literal("Black").alias("surName") + ); + + JpaSubQuery unionAllSubQuery = builder.unionAll(q1, q2); + JpaDerivedRoot mainQueryRoot = mainQuery.from( unionAllSubQuery ); + + mainQuery.multiselect( + builder.trim(mainQueryRoot.get("name")).alias("name"), + builder.trim(mainQueryRoot.get("surName")).alias("surName") + ); + mainQuery.orderBy( builder.asc(mainQueryRoot.get("name")) ); + + return entityManager.createQuery(mainQuery).getResultList(); + } + @Entity(name = "Person") public static class Person { @Id @@ -83,6 +137,8 @@ public static class Person { String name; + String surName; + @OneToMany List

    addresses =new ArrayList<>(); @@ -90,8 +146,13 @@ public Person() { } public Person(Integer id, String name) { + this(id,name,null); + } + + public Person(Integer id, String name, String surName) { this.id = id; this.name = name; + this.surName = surName; } public void addAddress(Address address){ @@ -107,6 +168,10 @@ public String getName() { return name; } + public String getSurName() { + return surName; + } + public List
    getAddresses() { return addresses; } From 3f7c26e4e589ecc8cfa9f3a7a1d5ba3253ca7314 Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Mon, 4 Mar 2024 23:15:44 +0100 Subject: [PATCH 176/321] HHH-17776 - Fix whitespace padding issues for literals in select expressions on Derby and HSQLDB Signed-off-by: Jan Schatteman --- .../java/org/hibernate/dialect/DerbySqlAstTranslator.java | 7 ++++++- .../java/org/hibernate/dialect/HSQLSqlAstTranslator.java | 7 ++++++- .../hibernate/sql/ast/spi/AbstractSqlAstTranslator.java | 2 +- .../orm/test/jpa/criteria/subquery/SubqueryTests.java | 4 ++-- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DerbySqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/DerbySqlAstTranslator.java index 09df01851045..05f8e4adcaa5 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DerbySqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DerbySqlAstTranslator.java @@ -167,7 +167,12 @@ protected void renderComparison(Expression lhs, ComparisonOperator operator, Exp @Override protected void renderSelectExpression(Expression expression) { - renderSelectExpressionWithCastedOrInlinedPlainParameters( expression ); + if ( isInSubquery() && expression instanceof Literal ) { + renderCasted( expression ); + } + else { + renderSelectExpressionWithCastedOrInlinedPlainParameters( expression ); + } } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLSqlAstTranslator.java index 96d36ae03643..77c24e667b7e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLSqlAstTranslator.java @@ -214,7 +214,12 @@ public void visitOffsetFetchClause(QueryPart queryPart) { @Override protected void renderSelectExpression(Expression expression) { - renderSelectExpressionWithCastedOrInlinedPlainParameters( expression ); + if ( isInSubquery() && expression instanceof Literal ) { + renderCasted( expression ); + } + else { + renderSelectExpressionWithCastedOrInlinedPlainParameters( expression ); + } } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index 6ea4be24aabb..39d3fc483c64 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -1922,7 +1922,7 @@ protected boolean isInRecursiveQueryPart() { .get( 1 ) == getCurrentQueryPart(); } - private boolean isInSubquery() { + protected boolean isInSubquery() { return statementStack.depth() > 1 && statementStack.getCurrent() instanceof SelectStatement && !( (SelectStatement) statementStack.getCurrent() ).getQueryPart().isRoot(); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/subquery/SubqueryTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/subquery/SubqueryTests.java index 334ee9aa6126..0204e2fa1460 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/subquery/SubqueryTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/subquery/SubqueryTests.java @@ -122,8 +122,8 @@ private List getEntities(EntityManager entityManager) { JpaDerivedRoot mainQueryRoot = mainQuery.from( unionAllSubQuery ); mainQuery.multiselect( - builder.trim(mainQueryRoot.get("name")).alias("name"), - builder.trim(mainQueryRoot.get("surName")).alias("surName") + mainQueryRoot.get("name").alias("name"), + mainQueryRoot.get("surName").alias("surName") ); mainQuery.orderBy( builder.asc(mainQueryRoot.get("name")) ); From defe7e4d951449e2d224d7e02d92eec27cd669ba Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Fri, 19 Jan 2024 15:22:55 +0100 Subject: [PATCH 177/321] HHH-17638 Add test for issue --- .../ignore/NotFoundManyToOneCountTest.java | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/notfound/ignore/NotFoundManyToOneCountTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/ignore/NotFoundManyToOneCountTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/ignore/NotFoundManyToOneCountTest.java new file mode 100644 index 000000000000..4dd2ec56f008 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/ignore/NotFoundManyToOneCountTest.java @@ -0,0 +1,152 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.notfound.ignore; + +import java.util.List; + +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Marco Belladelli + */ +@DomainModel( annotatedClasses = { + NotFoundManyToOneCountTest.MyEntity.class, + NotFoundManyToOneCountTest.AssociatedEntity.class +} ) +@SessionFactory +@Jira( "https://hibernate.atlassian.net/browse/HHH-17638" ) +public class NotFoundManyToOneCountTest { + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final AssociatedEntity associated1 = new AssociatedEntity( 1L, "associated_1" ); + session.persist( associated1 ); + session.persist( new MyEntity( 1L, associated1 ) ); + final AssociatedEntity associated2 = new AssociatedEntity( 2L, "associated_2" ); + session.persist( associated2 ); + session.persist( new MyEntity( 2L, associated2 ) ); + } ); + + scope.inTransaction( session -> session.createMutationQuery( "delete from AssociatedEntity where id = 1" ).executeUpdate() ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.createMutationQuery( "delete from MyEntity" ).executeUpdate(); + session.createMutationQuery( "delete from AssociatedEntity" ).executeUpdate(); + } ); + } + + @Test + public void testCountQuery(SessionFactoryScope scope) { + final String hql = "select %s from MyEntity e"; + final List resultList = scope.fromTransaction( session -> session.createQuery( String.format( + hql, + "e" + ), MyEntity.class ).getResultList() ); + final Long count = scope.fromTransaction( session -> session.createQuery( + String.format( hql, "count(e)" ), + Long.class + ).getSingleResult() ); + assertThat( resultList ).hasSize( count.intValue() ); + assertThat( resultList.stream().map( MyEntity::getId ) ).contains( 1L, 2L ); + } + + @Test + public void testCountQueryComparisonPredicate(SessionFactoryScope scope) { + final String hql = "select %s from MyEntity e where e.associated = :associated"; + final AssociatedEntity associated = scope.fromTransaction( session -> session.find( + AssociatedEntity.class, + 2L + ) ); + final List resultList = scope.fromTransaction( session -> session.createQuery( + String.format( hql, "e" ), + MyEntity.class + ).setParameter( "associated", associated ).getResultList() ); + final Long count = scope.fromTransaction( session -> session.createQuery( + String.format( hql, "count(e)" ), + Long.class + ).setParameter( "associated", associated ).getSingleResult() ); + assertThat( resultList ).hasSize( count.intValue() ); + assertThat( resultList.stream().map( MyEntity::getId ) ).contains( 2L ); + } + + @Test + public void testCountQueryInListPredicate(SessionFactoryScope scope) { + final String hql = "select %s from MyEntity e where e.associated in :associated"; + final AssociatedEntity associated = scope.fromTransaction( session -> session.find( + AssociatedEntity.class, + 2L + ) ); + final List resultList = scope.fromTransaction( session -> session.createQuery( + String.format( hql, "e" ), + MyEntity.class + ).setParameter( "associated", associated ).getResultList() ); + final Long count = scope.fromTransaction( session -> session.createQuery( + String.format( hql, "count(e)" ), + Long.class + ).setParameter( "associated", associated ).getSingleResult() ); + assertThat( resultList ).hasSize( count.intValue() ); + assertThat( resultList.stream().map( MyEntity::getId ) ).contains( 2L ); + } + + @Entity( name = "MyEntity" ) + public static class MyEntity { + @Id + private Long id; + + @ManyToOne + @NotFound( action = NotFoundAction.IGNORE ) + @JoinColumn( name = "associated_id" ) + private AssociatedEntity associated; + + public MyEntity() { + } + + public MyEntity(Long id, AssociatedEntity associated) { + this.id = id; + this.associated = associated; + } + + public Long getId() { + return id; + } + } + + @Entity( name = "AssociatedEntity" ) + public static class AssociatedEntity { + @Id + private Long id; + + private String name; + + public AssociatedEntity() { + } + + public AssociatedEntity(Long id, String name) { + this.id = id; + this.name = name; + } + } +} From 297652ab065cdc2f5ccc0d2c2857653ee551c304 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Fri, 19 Jan 2024 15:23:30 +0100 Subject: [PATCH 178/321] HHH-17638 Prevent reusing left joins for implicitly joined paths --- .../query/sqm/sql/BaseSqmToSqlAstConverter.java | 11 +++++++---- .../sql/ast/spi/SimpleFromClauseAccessImpl.java | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 6cb34fb9c212..304d187a21be 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -3629,7 +3629,10 @@ private X prepareReusablePath( if ( sqmPath instanceof SqmEntityValuedSimplePath || sqmPath instanceof SqmEmbeddedValuedSimplePath || sqmPath instanceof SqmAnyValuedSimplePath ) { - final TableGroup existingTableGroup = fromClauseIndex.findTableGroupForGetOrCreate( sqmPath.getNavigablePath() ); + final TableGroup existingTableGroup = fromClauseIndex.findTableGroupForGetOrCreate( + sqmPath.getNavigablePath(), + allowLeftJoins + ); if ( existingTableGroup == null ) { final TableGroup createdTableGroup = createTableGroup( getActualTableGroup( @@ -3882,9 +3885,9 @@ private TableGroup createTableGroup(TableGroup parentTableGroup, SqmPath join private boolean isMappedByOrNotFoundToOne(TableGroupJoinProducer joinProducer) { if ( joinProducer instanceof ToOneAttributeMapping ) { final ToOneAttributeMapping toOne = (ToOneAttributeMapping) joinProducer; - if ( toOne.hasNotFoundAction() || toOne.getReferencedPropertyName() != null ) { - return true; - } + return toOne.hasNotFoundAction() || + // ToOne( mappedBy = "..." ) always has a referenced property name and is the target side + ( toOne.getReferencedPropertyName() != null && toOne.getSideNature() == ForeignKeyDescriptor.Nature.TARGET ); } return false; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SimpleFromClauseAccessImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SimpleFromClauseAccessImpl.java index 2ad558cc3543..d6917a2df418 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SimpleFromClauseAccessImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SimpleFromClauseAccessImpl.java @@ -9,11 +9,14 @@ import java.util.HashMap; import java.util.Map; +import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.spi.NavigablePath; +import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlTreeCreationLogger; import org.hibernate.sql.ast.tree.from.CorrelatedTableGroup; import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.jboss.logging.Logger; @@ -83,6 +86,17 @@ public TableGroup findTableGroupForGetOrCreate(NavigablePath navigablePath) { } } + public TableGroup findTableGroupForGetOrCreate(NavigablePath navigablePath, boolean allowLeftJoins) { + final TableGroup tableGroup = findTableGroupForGetOrCreate( navigablePath ); + if ( !allowLeftJoins && tableGroup != null && navigablePath.getParent() != null + && CollectionPart.Nature.fromNameExact( navigablePath.getLocalName() ) == null ) { + // This is an implicitly joined path, do not reuse existing table group if it's left joined + final TableGroupJoin join = findTableGroup( navigablePath.getParent() ).findTableGroupJoin( tableGroup ); + return join != null && join.getJoinType() == SqlAstJoinType.LEFT ? null : tableGroup; + } + return tableGroup; + } + private TableGroup getCorrelatedTableGroup(TableGroup tableGroup) { if ( tableGroup instanceof CorrelatedTableGroup ) { return getCorrelatedTableGroup( ( (CorrelatedTableGroup) tableGroup ).getCorrelatedTableGroup() ); From 7e2a49f54230e742e531a810f2a76f9514145895 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Thu, 7 Mar 2024 16:14:01 +0100 Subject: [PATCH 179/321] HHH-17763 Run test only on H2 --- .../orm/test/schemafilter/SchemaFilterProviderTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemafilter/SchemaFilterProviderTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/schemafilter/SchemaFilterProviderTest.java index 9a7278f5bc77..28a89898121d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/schemafilter/SchemaFilterProviderTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/schemafilter/SchemaFilterProviderTest.java @@ -14,6 +14,7 @@ import org.hibernate.boot.registry.internal.StandardServiceRegistryImpl; import org.hibernate.cfg.Environment; import org.hibernate.cfg.SchemaToolingSettings; +import org.hibernate.dialect.H2Dialect; import org.hibernate.internal.util.PropertiesHelper; import org.hibernate.mapping.Table; import org.hibernate.tool.schema.internal.ExceptionHandlerHaltImpl; @@ -28,6 +29,7 @@ import org.hibernate.testing.ServiceRegistryBuilder; import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.transaction.TransactionUtil; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; @@ -44,6 +46,7 @@ */ @TestInstance( TestInstance.Lifecycle.PER_CLASS ) @Jira( "https://hibernate.atlassian.net/browse/HHH-17763" ) +@RequiresDialect(H2Dialect.class) public class SchemaFilterProviderTest { @Test public void testValidationDefaultProvider() { From 2e2d0b696b53778bb69ca5c90d92176bb5c74305 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Thu, 7 Mar 2024 16:15:11 +0100 Subject: [PATCH 180/321] HHH-17823 Add FK to identifier in HQL parser --- .../org/hibernate/grammars/hql/HqlParser.g4 | 1 + .../test/hql/SelectWithFkInPackageTest.java | 110 ++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/hql/SelectWithFkInPackageTest.java diff --git a/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 b/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 index 224fba7d99dc..b7d1df0c3db5 100644 --- a/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 +++ b/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 @@ -1635,6 +1635,7 @@ rollup | FETCH | FILTER | FIRST + | FK | FOLLOWING | FOR | FORMAT diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/SelectWithFkInPackageTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/SelectWithFkInPackageTest.java new file mode 100644 index 000000000000..8af4431717f6 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/SelectWithFkInPackageTest.java @@ -0,0 +1,110 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.orm.test.hql; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; + +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jpa; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +@Jpa( + annotatedClasses = { + SelectWithFkInPackageTest.Person.class, + SelectWithFkInPackageTest.Book.class + } +) +class SelectWithFkInPackageTest { + + @BeforeAll + void init(EntityManagerFactoryScope scope) { + scope.inTransaction( entityManager -> { + entityManager.persist( new Person( "name" ) ); + entityManager.persist( new Book( "title" ) ); + } ); + } + + @AfterAll + void tearDown(EntityManagerFactoryScope scope) throws Exception { + scope.inTransaction( entityManager -> { + entityManager.createQuery( "delete from org.fk.entity.Person" ); + entityManager.createQuery( "delete from org.right.entity.Book" ); + } ); + } + + @Test + void selectWithFk(EntityManagerFactoryScope scope) { + scope.inTransaction( entityManager -> { + List people = entityManager.createQuery( "from org.fk.entity.Person", Person.class ) + .getResultList(); + + assertThat( people ).hasSize( 1 ); + } ); + } + + @Test + void selectNoFk(EntityManagerFactoryScope scope) { + scope.inTransaction( entityManager -> { + List books = entityManager.createQuery( "from org.right.entity.Book", Book.class ) + .getResultList(); + + assertThat( books ).hasSize( 1 ); + } ); + } + + @Entity(name = "org.fk.entity.Person") + @Table(name = "person") + public static class Person { + + @Id + @GeneratedValue + public Long id; + + @Column + public String name; + + public Person() { + } + + public Person(String name) { + this.name = name; + } + } + + @Entity(name = "org.right.entity.Book") + @Table(name = "book") + public static class Book { + + @Id + @GeneratedValue + public Long id; + + @Column + public String title; + + public Book() { + } + + public Book(String title) { + this.title = title; + } + } + +} From 5b8f326839c9b8d81ff5579b1bc47cc593ef8773 Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Wed, 31 Jan 2024 00:19:06 +0100 Subject: [PATCH 181/321] HHH-17671 - Add test for issue Signed-off-by: Jan Schatteman --- .../jpa/compliance/CriteriaIsNullTest.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/CriteriaIsNullTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/CriteriaIsNullTest.java index f1198d2e5d0f..a466a0c03251 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/CriteriaIsNullTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/CriteriaIsNullTest.java @@ -8,8 +8,13 @@ import java.util.List; +import org.hibernate.query.sqm.tree.SqmCopyContext; +import org.hibernate.query.sqm.tree.predicate.SqmPredicate; + import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import jakarta.persistence.Entity; @@ -18,6 +23,7 @@ import jakarta.persistence.Table; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -69,6 +75,21 @@ public void testIsNullInWhereClause(EntityManagerFactoryScope scope) { } + @Test + @JiraKey( "HHH-17671" ) + public void testPredicateCopy(EntityManagerFactoryScope scope) { + scope.inEntityManager( + entityManager -> { + CriteriaBuilder builder = entityManager.getCriteriaBuilder(); + CriteriaQuery c = builder.createQuery(Account.class); + Root r = c.from(Account.class); + Predicate predicate = builder.isNull( r.get( "amount")); + c.where(predicate); + Assertions.assertDoesNotThrow( () -> ( (SqmPredicate) c.getRestriction() ).copy( SqmCopyContext.simpleContext()) ); + } + ); + } + @Entity(name = "Person") public static class Person { From 7da31dfd6d83bd5647413d271873645950e71778 Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Wed, 31 Jan 2024 00:19:49 +0100 Subject: [PATCH 182/321] HHH-17671 - Fix issue Signed-off-by: Jan Schatteman --- .../hibernate/query/sqm/tree/domain/AbstractSqmPath.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java index 2fae3a9beb85..7ccdd46fad69 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java @@ -63,12 +63,6 @@ protected AbstractSqmPath( protected void copyTo(AbstractSqmPath target, SqmCopyContext context) { assert navigablePathsMatch( target ); super.copyTo( target, context ); - if ( reusablePaths != null ) { - target.reusablePaths = new HashMap<>( reusablePaths.size() ); - for ( Map.Entry> entry : reusablePaths.entrySet() ) { - target.reusablePaths.put( entry.getKey(), entry.getValue().copy( context ) ); - } - } } // meant for assertions only From b170f2deddbb28b026c16684110ddbc95f453b40 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 11 Mar 2024 01:43:48 +0100 Subject: [PATCH 183/321] HHH-17825 fix npe for single-column @UniqueConstraint Note that HHH-17132 already attempted to fix the reported problem, but the fix introduced a new bug, and NPE. --- .../unique/CreateTableUniqueDelegate.java | 19 ++++-- .../MultiUniqueConstraintNameTest.java | 61 +++++++++++++++++ .../UniqueConstraintNameTest.java | 65 +++++++++++++++++++ 3 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/MultiUniqueConstraintNameTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/UniqueConstraintNameTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/CreateTableUniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/CreateTableUniqueDelegate.java index ddbeb643cf8e..b0e893717aab 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/CreateTableUniqueDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/CreateTableUniqueDelegate.java @@ -80,10 +80,21 @@ protected void appendUniqueConstraint(StringBuilder fragment, UniqueKey uniqueKe } private static boolean isSingleColumnUnique(Table table, UniqueKey uniqueKey) { - return uniqueKey.getColumns().size() == 1 - // Since columns are created on demand in IndexBinder.createColumn, - // we also have to check if the "real" column is unique to be safe - && ( uniqueKey.getColumn( 0 ).isUnique() || table.getColumn( uniqueKey.getColumn( 0 ) ).isUnique() ); + if ( uniqueKey.getColumns().size() == 1) { + // Since columns are created on demand in IndexBinder.createColumn, + // we also have to check if the "real" column is unique to be safe + final Column uniqueKeyColumn = uniqueKey.getColumn(0); + if ( uniqueKeyColumn.isUnique() ) { + return true; + } + else { + final Column column = table.getColumn( uniqueKeyColumn ); + return column != null && column.isUnique(); + } + } + else { + return false; + } } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/MultiUniqueConstraintNameTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/MultiUniqueConstraintNameTest.java new file mode 100644 index 000000000000..078cceeeef70 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/MultiUniqueConstraintNameTest.java @@ -0,0 +1,61 @@ +package org.hibernate.orm.test.schemaupdate.uniqueconstraint; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +@SessionFactory +@DomainModel(annotatedClasses = {MultiUniqueConstraintNameTest.MyEntity.class, MultiUniqueConstraintNameTest.MyOtherEntity.class}) +public class MultiUniqueConstraintNameTest { + + @Test void test(SessionFactoryScope scope) { + scope.getSessionFactory(); + } + + @Entity + @Table(name = "my_entity", + uniqueConstraints = + @UniqueConstraint( + name = "my_other_entity_id_unique", + columnNames = {"my_other_entity_id1","my_other_entity_id2"} + ), + indexes = @Index(name = "some_long_index", columnList = "some_long")) + static class MyEntity { + + @Id + long id1; + @Id + long id2; + + @OneToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "my_other_entity_id1") + @JoinColumn(name = "my_other_entity_id2") + private MyOtherEntity myOtherEntity; + + @Column(name = "some_long") + private long someLong; + + } + + @Entity + @Table(name = "my_other_entity") + static class MyOtherEntity { + @Id + long id1; + @Id + long id2; + + @Column(name = "some_string") + private String someString; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/UniqueConstraintNameTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/UniqueConstraintNameTest.java new file mode 100644 index 000000000000..de0e073cd4f3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/UniqueConstraintNameTest.java @@ -0,0 +1,65 @@ +package org.hibernate.orm.test.schemaupdate.uniqueconstraint; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +@SessionFactory +@DomainModel(annotatedClasses = {UniqueConstraintNameTest.MyEntity.class, UniqueConstraintNameTest.MyOtherEntity.class}) +@JiraKey("HHH-17825") +@JiraKey("HHH-17132") +public class UniqueConstraintNameTest { + + @Test void test(SessionFactoryScope scope) { + scope.getSessionFactory(); + } + + @Entity + @Table(name = "my_entity", + uniqueConstraints = + @UniqueConstraint( + name = "my_other_entity_id_unique", + columnNames = "my_other_entity_id" + ), + indexes = @Index(name = "some_long_index", columnList = "some_long")) + static class MyEntity { + + @Id + @GeneratedValue + long id; + + @OneToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "my_other_entity_id", + updatable = false, + foreignKey = @ForeignKey(name = "FK_moe")) + private MyOtherEntity myOtherEntity; + + @Column(name = "some_long") + private long someLong; + + } + + @Entity + @Table(name = "my_other_entity") + static class MyOtherEntity { + @Id + @GeneratedValue + long id; + + @Column(name = "some_string") + private String someString; + } +} From 16a3bfb20cb26664efebf1a4cc380a70be67bd72 Mon Sep 17 00:00:00 2001 From: Cedomir Igaly Date: Wed, 6 Dec 2023 10:40:41 +0100 Subject: [PATCH 184/321] HHH-17600 - Changed test class to really test handling of ListIndexBase annotation --- .../OrderColumnListIndexBaseTest.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/OrderColumnListIndexBaseTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/OrderColumnListIndexBaseTest.java index 56fc1b0c12d2..6d98e042baaf 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/OrderColumnListIndexBaseTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/OrderColumnListIndexBaseTest.java @@ -6,6 +6,8 @@ */ package org.hibernate.orm.test.mapping.collections; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -23,7 +25,9 @@ import org.junit.Test; +import static org.hibernate.testing.transaction.TransactionUtil.doInAutoCommit; import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; /** * @author Vlad Mihalcea @@ -48,6 +52,53 @@ public void testLifecycle() { person.addPhone(new Phone(2L, "mobile", "072-122-9876")); //end::collections-customizing-ordered-list-ordinal-persist-example[] }); + doInAutoCommit( st -> { + try (ResultSet rs = st.executeQuery( "select id, order_id from Phone" )) { + while ( rs.next() ) { + final long id = rs.getLong( 1 ); + if ( id == 1 ) { + assertEquals( 100, rs.getInt( 2 ) ); + } + else if ( id == 2 ) { + assertEquals( 101, rs.getInt( 2 ) ); + } + } + } + catch (SQLException e) { + throw new RuntimeException( e ); + } + } ); + doInJPA(this::entityManagerFactory, entityManager -> { + Person person = entityManager.find( Person.class, 1L); + person.addPhone(new Phone(3L, "fax", "099-234-9876")); + entityManager.persist(person); + }); + doInAutoCommit( st -> { + try (ResultSet rs = st.executeQuery( "select id, order_id from Phone" )) { + while ( rs.next() ) { + final long id = rs.getLong( 1 ); + if ( id == 1 ) { + assertEquals( 100, rs.getInt( 2 ) ); + } + else if ( id == 2 ) { + assertEquals( 101, rs.getInt( 2 ) ); + } + else if ( id == 3 ) { + assertEquals( 102, rs.getInt( 2 ) ); + } + } + } + catch (SQLException e) { + throw new RuntimeException( e ); + } + } ); + doInJPA(this::entityManagerFactory, entityManager -> { + Person person = entityManager.find( Person.class, 1L); + final List phones = person.getPhones(); + assertEquals( Long.valueOf( 1L ), phones.get( 0 ).getId() ); + assertEquals( Long.valueOf( 2L ), phones.get( 1 ).getId() ); + assertEquals( Long.valueOf( 3L ), phones.get( 2 ).getId() ); + }); } @Entity(name = "Person") From e1d326ef3e0a2f97cda44ce6a12d9a0cd70d6751 Mon Sep 17 00:00:00 2001 From: Cedomir Igaly Date: Wed, 6 Dec 2023 10:41:23 +0100 Subject: [PATCH 185/321] HHH-17600 - Properly setting next index if ListIndexBase annotation is present --- .../org/hibernate/persister/collection/OneToManyPersister.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java index 1caf208f6be1..5451eda1d21b 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java @@ -233,7 +233,8 @@ && hasIndex() final JdbcValueBindings jdbcValueBindings = mutationExecutor.getJdbcValueBindings(); try { - int nextIndex = resetIndex ? 0 : getSize( key, session ); + int nextIndex = ( resetIndex ? 0 : getSize( key, session ) ) + + Math.max( getAttributeMapping().getIndexMetadata().getListIndexBase(), 0 ); while ( entries.hasNext() ) { final Object entry = entries.next(); From d49ad70ad29e455660deff346bf8a4766656d631 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 6 Feb 2024 13:02:49 +0100 Subject: [PATCH 186/321] HHH-17711 Add test for issue --- ...nedInheritanceTreatedJoinNullnessTest.java | 217 ++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/JoinedInheritanceTreatedJoinNullnessTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/JoinedInheritanceTreatedJoinNullnessTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/JoinedInheritanceTreatedJoinNullnessTest.java new file mode 100644 index 000000000000..e8a049144a34 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/JoinedInheritanceTreatedJoinNullnessTest.java @@ -0,0 +1,217 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.inheritance; + +import java.util.List; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.JoinType; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Marco Belladelli + */ +@DomainModel( annotatedClasses = { + JoinedInheritanceTreatedJoinNullnessTest.AbstractCompany.class, + JoinedInheritanceTreatedJoinNullnessTest.AbstractDcCompany.class, + JoinedInheritanceTreatedJoinNullnessTest.DcCompany.class, + JoinedInheritanceTreatedJoinNullnessTest.DcCompanySeed.class, + JoinedInheritanceTreatedJoinNullnessTest.RcCompany.class, + JoinedInheritanceTreatedJoinNullnessTest.RcCompanyUser.class, +} ) +@SessionFactory +@Jira( "https://hibernate.atlassian.net/browse/HHH-17711" ) +public class JoinedInheritanceTreatedJoinNullnessTest { + @Test + public void testRootIsNotNull(SessionFactoryScope scope) { + executeQuery( scope, 2, (cb, cq, root) -> cq.where( cb.isNotNull( cb.treat( root, DcCompanySeed.class ) ) ) ); + } + + @Test + public void testJoinIsNotNull(SessionFactoryScope scope) { + executeQuery( scope, 1, (cb, cq, root) -> cq.where( cb.isNotNull( + cb.treat( root, DcCompanySeed.class ).join( "invitedBy", JoinType.LEFT ) + ) ) ); + } + + @Test + public void testNestedJoinIsNotNull(SessionFactoryScope scope) { + executeQuery( scope, 1, (cb, cq, root) -> cq.where( cb.isNotNull( + cb.treat( root, DcCompanySeed.class ) + .join( "invitedBy", JoinType.LEFT ) + .join( "rcCompany", JoinType.LEFT ) + ) ) ); + } + + @Test + public void testEitherJoinNotNull(SessionFactoryScope scope) { + executeQuery( scope, 2, (cb, cq, root) -> { + final Predicate seedPredicate = cb.and( + cb.equal( root.get( "displayName" ), "test" ), + cb.isNotNull( cb.treat( root, DcCompanySeed.class ).join( "invitedBy", JoinType.LEFT ).get( "rcCompany" ) ) + ); + final Predicate dcPredicate = cb.and( + cb.equal( root.get( "displayName" ), "test" ), + cb.isNotNull( cb.treat( root, DcCompany.class ).get( "rcCompany" ) ) + ); + cq.where( cb.or( seedPredicate, dcPredicate ) ); + } ); + } + + private void executeQuery(SessionFactoryScope scope, int expectedResults, CriteriaConsumer consumer) { + // Test id selection + scope.inTransaction( session -> { + final CriteriaBuilder cb = session.getCriteriaBuilder(); + final CriteriaQuery cq = cb.createQuery( Long.class ); + final Root root = cq.from( AbstractDcCompany.class ); + consumer.apply( cb, cq, root ); + final List resultList = session.createQuery( cq.select( root.get( "id" ) ) ).getResultList(); + assertThat( resultList ).hasSize( expectedResults ); + } ); + // Test root selection + scope.inTransaction( session -> { + final CriteriaBuilder cb = session.getCriteriaBuilder(); + final CriteriaQuery cq = cb.createQuery( AbstractDcCompany.class ); + final Root root = cq.from( AbstractDcCompany.class ); + consumer.apply( cb, cq, root ); + final List resultList = session.createQuery( cq.select( root ) ).getResultList(); + assertThat( resultList ).hasSize( expectedResults ); + } ); + } + + public interface CriteriaConsumer { + void apply(CriteriaBuilder cb, CriteriaQuery cq, Root root); + } + + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final RcCompany rc = new RcCompany( "rc" ); + session.persist( rc ); + final RcCompanyUser rcu = new RcCompanyUser( rc ); + session.persist( rcu ); + session.persist( new DcCompanySeed( "test", rcu ) ); + session.persist( new DcCompanySeed( "test", null ) ); + session.persist( new DcCompany( "test", rc ) ); + session.persist( new DcCompany( "test", null ) ); + } ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.createMutationQuery( "delete from DcCompanySeed" ).executeUpdate(); + session.createMutationQuery( "delete from RcCompanyUser" ).executeUpdate(); + session.createMutationQuery( "delete from DcCompany" ).executeUpdate(); + session.createMutationQuery( "delete from AbstractCompany" ).executeUpdate(); + } ); + } + + @Entity( name = "AbstractCompany" ) + @Inheritance( strategy = InheritanceType.JOINED ) + public static abstract class AbstractCompany { + @Id + @GeneratedValue + private Long id; + + @Column + private String displayName; + + public AbstractCompany() { + } + + public AbstractCompany(String displayName) { + this.displayName = displayName; + } + } + + @Entity( name = "AbstractDcCompany" ) + @Inheritance( strategy = InheritanceType.JOINED ) + public abstract static class AbstractDcCompany extends AbstractCompany { + public AbstractDcCompany() { + } + + public AbstractDcCompany(String displayName) { + super( displayName ); + } + } + + @Entity( name = "DcCompany" ) + public static class DcCompany extends AbstractDcCompany { + @ManyToOne( fetch = FetchType.LAZY ) + private RcCompany rcCompany; + + public DcCompany() { + } + + public DcCompany(String displayName, RcCompany rcCompany) { + super( displayName ); + this.rcCompany = rcCompany; + } + } + + @Entity( name = "DcCompanySeed" ) + public static class DcCompanySeed extends AbstractDcCompany { + @ManyToOne + private RcCompanyUser invitedBy; + + public DcCompanySeed() { + } + + public DcCompanySeed(String displayName, RcCompanyUser invitedBy) { + super( displayName ); + this.invitedBy = invitedBy; + } + } + + @Entity( name = "RcCompany" ) + public static class RcCompany extends AbstractCompany { + public RcCompany() { + } + + public RcCompany(String displayName) { + super( displayName ); + } + } + + @Entity( name = "RcCompanyUser" ) + public static class RcCompanyUser { + @Id + @GeneratedValue + private Long id; + + @ManyToOne( fetch = FetchType.LAZY ) + private RcCompany rcCompany; + + public RcCompanyUser() { + } + + public RcCompanyUser(RcCompany rcCompany) { + this.rcCompany = rcCompany; + } + } +} From 037336ea83a0689d34d9082159ae71ba47444c4a Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 6 Feb 2024 13:04:33 +0100 Subject: [PATCH 187/321] HHH-17711 Fix table reference join resolution for joined subtype --- .../engine/jdbc/internal/BasicFormatterImpl.java | 1 + .../persister/entity/AbstractEntityPersister.java | 14 ++------------ 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/BasicFormatterImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/BasicFormatterImpl.java index 99949e6a8a48..f7cf8777ada3 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/BasicFormatterImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/BasicFormatterImpl.java @@ -279,6 +279,7 @@ private void logical() { private void endCase() { afterBeginBeforeEnd = false; decrementIndent(); + decrementIndent(); logical(); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 9aa98b3139ab..39bcabe75ea9 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -1286,17 +1286,7 @@ public String getSqlAliasStem() { @Override public boolean containsTableReference(String tableExpression) { - if ( getTableName().equals( tableExpression ) ) { - return true; - } - else { - for ( int i = 0; i < getSubclassTableSpan(); i++ ) { - if ( getSubclassTableName( i ).equals( tableExpression ) ) { - return true; - } - } - return false; - } + return contains( getSubclassTableNames(), tableExpression ); } @Override @@ -3070,7 +3060,7 @@ public TableGroup createRootTableGroup( rootTableReference, true, sqlAliasBase, - (tableExpression) -> contains( getSubclassTableNames(), tableExpression ), + (tableExpression) -> getRootEntityDescriptor().containsTableReference( tableExpression ), (tableExpression, tg) -> { final String[] subclassTableNames = getSubclassTableNames(); for ( int i = 0; i < subclassTableNames.length; i++ ) { From d1f1e6e8b9adb82d24b92868370eaba42312ef5f Mon Sep 17 00:00:00 2001 From: Laurent SCHOELENS <61973605+laurentschoelens@users.noreply.github.com> Date: Tue, 19 Dec 2023 14:59:44 +0100 Subject: [PATCH 188/321] HHH-17579 fix classloader issues with JAXBContext since JDK11 --- .../jpamodelgen/util/xml/XmlParserHelper.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/xml/XmlParserHelper.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/xml/XmlParserHelper.java index b73cc27a107f..e36ed507ec3e 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/xml/XmlParserHelper.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/xml/XmlParserHelper.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import javax.tools.FileObject; @@ -120,7 +121,14 @@ public T getJaxbRoot(InputStream stream, Class clazz, Schema schema) ContextProvidingValidationEventHandler handler = new ContextProvidingValidationEventHandler(); try { staxEventReader = new JpaNamespaceTransformingEventReader( staxEventReader ); - JAXBContext jaxbContext = JAXBContext.newInstance( ObjectFactory.class ); + + // Class.getClassLoader() may return null if the class was loaded by the bootstrap class loader, + // but since we don't expect the annotation processor to be loaded by that class loader, + // we expect the return to be non-null and hence cast + ClassLoader cl = NullnessUtil.castNonNull( ObjectFactory.class.getClassLoader() ); + String packageName = NullnessUtil.castNonNull( ObjectFactory.class.getPackage() ).getName(); + JAXBContext jaxbContext = JAXBContext.newInstance( packageName, cl ); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); unmarshaller.setSchema( schema ); unmarshaller.setEventHandler( handler ); From c778565c7b8c80125cd7eff6f0a0c27abd22badf Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 15 Mar 2024 13:43:00 +0100 Subject: [PATCH 189/321] HHH-17830 Fix rendering custom predicate for element collection joins --- .../ast/internal/LoaderSelectBuilder.java | 9 +--- .../internal/PluralAttributeMappingImpl.java | 9 +--- .../sqm/sql/BaseSqmToSqlAstConverter.java | 21 ++++----- .../ast/internal/TableGroupJoinHelper.java | 46 +++++++++++++++++++ .../jpa/ql/JoinTableOptimizationTest.java | 31 +++++++++++++ 5 files changed, 89 insertions(+), 27 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/internal/TableGroupJoinHelper.java diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java index 8e46b9c1b8c2..3c9607e2afa5 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java @@ -49,6 +49,7 @@ import org.hibernate.spi.EntityIdentifierNavigablePath; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.SqlAstJoinType; +import org.hibernate.sql.ast.internal.TableGroupJoinHelper; import org.hibernate.sql.ast.spi.AliasCollector; import org.hibernate.sql.ast.spi.FromClauseAccess; import org.hibernate.sql.ast.spi.SimpleFromClauseAccessImpl; @@ -711,13 +712,7 @@ private void applyFiltering( final TableGroupJoin pluralTableGroupJoin = parentTableGroup.findTableGroupJoin( tableGroup ); assert pluralTableGroupJoin != null; - final TableGroupJoin joinForPredicate; - if ( !tableGroup.getNestedTableGroupJoins().isEmpty() || tableGroup.getTableGroupJoins().isEmpty() ) { - joinForPredicate = pluralTableGroupJoin; - } - else { - joinForPredicate = tableGroup.getTableGroupJoins().get( tableGroup.getTableGroupJoins().size() - 1 ); - } + final TableGroupJoin joinForPredicate = TableGroupJoinHelper.determineJoinForPredicateApply( pluralTableGroupJoin ); pluralAttributeMapping.applyBaseRestrictions( joinForPredicate::applyPredicate, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java index 456ac39e09f9..0a7aff3a13c9 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java @@ -49,6 +49,7 @@ import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.SqlAstJoinType; +import org.hibernate.sql.ast.internal.TableGroupJoinHelper; import org.hibernate.sql.ast.spi.FromClauseAccess; import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAliasStemHelper; @@ -752,13 +753,7 @@ public TableGroupJoin createTableGroupJoin( collectionPredicateCollector.getPredicate() ); if ( predicateCollector != collectionPredicateCollector ) { - final TableGroupJoin joinForPredicate; - if ( !tableGroup.getNestedTableGroupJoins().isEmpty() || tableGroup.getTableGroupJoins().isEmpty() ) { - joinForPredicate = tableGroupJoin; - } - else { - joinForPredicate = tableGroup.getTableGroupJoins().get( tableGroup.getTableGroupJoins().size() - 1 ); - } + final TableGroupJoin joinForPredicate = TableGroupJoinHelper.determineJoinForPredicateApply( tableGroupJoin ); joinForPredicate.applyPredicate( predicateCollector.getPredicate() ); } return tableGroupJoin; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 304d187a21be..cb7558ee35b9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -267,6 +267,7 @@ import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlTreeCreationException; import org.hibernate.sql.ast.SqlTreeCreationLogger; +import org.hibernate.sql.ast.internal.TableGroupJoinHelper; import org.hibernate.sql.ast.spi.FromClauseAccess; import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator; @@ -3364,13 +3365,9 @@ private TableGroup consumeAttributeJoin( } } + // Implicit joins in the predicate might alter the nested table group joins, + // so defer determination of the join for predicate until after the predicate was visited final TableGroupJoin joinForPredicate; - if ( !joinedTableGroup.getNestedTableGroupJoins().isEmpty() || joinedTableGroup.getTableGroupJoins().isEmpty() ) { - joinForPredicate = joinedTableGroupJoin; - } - else { - joinForPredicate = joinedTableGroup.getTableGroupJoins().get( joinedTableGroup.getTableGroupJoins().size() - 1 ); - } // add any additional join restrictions if ( sqmJoin.getJoinPredicate() != null ) { @@ -3381,6 +3378,7 @@ private TableGroup consumeAttributeJoin( final SqmJoin oldJoin = currentlyProcessingJoin; currentlyProcessingJoin = sqmJoin; final Predicate predicate = visitNestedTopLevelPredicate( sqmJoin.getJoinPredicate() ); + joinForPredicate = TableGroupJoinHelper.determineJoinForPredicateApply( joinedTableGroupJoin ); // If translating the join predicate didn't initialize the table group, // we can safely apply it on the collection table group instead if ( joinForPredicate.getJoinedGroup().isInitialized() ) { @@ -3391,6 +3389,9 @@ private TableGroup consumeAttributeJoin( } currentlyProcessingJoin = oldJoin; } + else { + joinForPredicate = TableGroupJoinHelper.determineJoinForPredicateApply( joinedTableGroupJoin ); + } // Since joins on treated paths will never cause table pruning, we need to add a join condition for the treat if ( sqmJoin.getLhs() instanceof SqmTreatedPath ) { final SqmTreatedPath treatedPath = (SqmTreatedPath) sqmJoin.getLhs(); @@ -8312,13 +8313,7 @@ public ImmutableFetchList visitFetches(FetchParent fetchParent) { final TableGroupJoin pluralTableGroupJoin = parentTableGroup.findTableGroupJoin( tableGroup ); assert pluralTableGroupJoin != null; - final TableGroupJoin joinForPredicate; - if ( !tableGroup.getNestedTableGroupJoins().isEmpty() || tableGroup.getTableGroupJoins().isEmpty() ) { - joinForPredicate = pluralTableGroupJoin; - } - else { - joinForPredicate = tableGroup.getTableGroupJoins().get( tableGroup.getTableGroupJoins().size() - 1 ); - } + final TableGroupJoin joinForPredicate = TableGroupJoinHelper.determineJoinForPredicateApply( pluralTableGroupJoin ); joinForPredicate.applyPredicate( predicate ); }, tableGroup, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/TableGroupJoinHelper.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/TableGroupJoinHelper.java new file mode 100644 index 000000000000..ae08030149ac --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/TableGroupJoinHelper.java @@ -0,0 +1,46 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html. + */ +package org.hibernate.sql.ast.internal; + +import org.hibernate.metamodel.mapping.internal.EmbeddedCollectionPart; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableGroupJoin; +import org.hibernate.sql.ast.tree.from.VirtualTableGroup; + +public class TableGroupJoinHelper { + + /** + * Determine the {@link TableGroupJoin} to which a custom {@code ON} clause predicate should be applied to. + * This is supposed to be called right after construction of a {@link TableGroupJoin}. + * This should also be called after a {@link org.hibernate.query.sqm.tree.predicate.SqmPredicate} is translated to a + * {@link org.hibernate.sql.ast.tree.predicate.Predicate}, because that translation might cause nested joins to be + * added to the table group of the join. + */ + public static TableGroupJoin determineJoinForPredicateApply(TableGroupJoin mainTableGroupJoin) { + final TableGroup mainTableGroup = mainTableGroupJoin.getJoinedGroup(); + if ( !mainTableGroup.getNestedTableGroupJoins().isEmpty() || mainTableGroup.getTableGroupJoins().isEmpty() ) { + // Always apply a predicate on the main table group join if it has nested table group joins or no joins + return mainTableGroupJoin; + } + else { + // If the main table group has just regular table group joins, + // prefer to apply predicates on the last table group join + final TableGroupJoin lastTableGroupJoin = mainTableGroup.getTableGroupJoins() + .get( mainTableGroup.getTableGroupJoins().size() - 1 ); + if ( lastTableGroupJoin.getJoinedGroup().getModelPart() instanceof EmbeddedCollectionPart ) { + // If the table group join refers to an embedded collection part, + // then the underlying table group *is* the main table group. + // Applying predicates on the join referring to the virtual table group would be a problem though, + // because these predicates will never be rendered. So use the main table group join in that case + assert lastTableGroupJoin.getJoinedGroup() instanceof VirtualTableGroup + && ( (VirtualTableGroup) lastTableGroupJoin.getJoinedGroup() ).getUnderlyingTableGroup() == mainTableGroup; + return mainTableGroupJoin; + } + return lastTableGroupJoin; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/JoinTableOptimizationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/JoinTableOptimizationTest.java index 4fd3a75b756b..9f79f5066e00 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/JoinTableOptimizationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/JoinTableOptimizationTest.java @@ -11,11 +11,15 @@ import org.hibernate.testing.TestForIssue; import org.hibernate.testing.jdbc.SQLStatementInspector; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import jakarta.persistence.CollectionTable; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Embeddable; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.JoinTable; @@ -123,6 +127,26 @@ public void testLeftJoinCustomOnClause(SessionFactoryScope scope) { ); } + @Test + @JiraKey("HHH-17830") + public void testElementCollectionJoinCustomOnClause(SessionFactoryScope scope) { + SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); + statementInspector.clear(); + scope.inTransaction( + s -> { + s.createQuery( "select p.text from Document d join d.pages p on p.text is not null" ).list(); + statementInspector.assertExecutedCount( 1 ); + Assertions.assertEquals( + "select p1_0.text " + + "from Document d1_0 " + + "join document_pages p1_0 on d1_0.id=p1_0.Document_id and p1_0.text is not null", + statementInspector.getSqlQueries().get( 0 ), + "Join condition was wrongly removed" + ); + } + ); + } + @Entity(name = "Document") public static class Document { @Id @@ -131,6 +155,9 @@ public static class Document { @OneToMany @JoinTable(name = "people") Set people; + @ElementCollection + @CollectionTable(name = "document_pages") + Set pages; } @Entity(name = "Person") public static class Person { @@ -138,4 +165,8 @@ public static class Person { Long id; String name; } + @Embeddable + public static class Page { + String text; + } } From 1d89c31e5ebadb6ed126d71f3c53fa17197ac6e1 Mon Sep 17 00:00:00 2001 From: subijayb Date: Tue, 20 Feb 2024 16:51:16 +0530 Subject: [PATCH 190/321] HHH-17743 Allow updates outside transaction --- .../query/sqm/internal/QuerySqmImpl.java | 1 - .../flush/NonTransactionalDataAccessTest.java | 24 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java index d1aacc3040a6..85b0ba11bb0d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java @@ -701,7 +701,6 @@ protected void verifyUpdate() { } protected int doExecuteUpdate() { - getSession().prepareForQueryExecution( true ); return resolveNonSelectQueryPlan().executeUpdate( this ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/flush/NonTransactionalDataAccessTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/flush/NonTransactionalDataAccessTest.java index c9e50a1fb1c8..92cebbe27293 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/flush/NonTransactionalDataAccessTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/flush/NonTransactionalDataAccessTest.java @@ -7,14 +7,17 @@ package org.hibernate.orm.test.flush; import jakarta.persistence.Entity; +import jakarta.persistence.EntityManager; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; +import jakarta.persistence.NamedQuery; import jakarta.persistence.Table; import jakarta.persistence.TransactionRequiredException; import org.hibernate.Session; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; +import org.hibernate.query.sqm.internal.QuerySqmImpl; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @@ -24,6 +27,7 @@ import static org.hamcrest.core.IsNot.not; import static org.hamcrest.core.IsNull.nullValue; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertEquals; /** * @author Andrea Boriero @@ -130,8 +134,28 @@ public void testFlushOutOfTransaction() throws Exception { } } + @Test + public void hhh17743Test() throws Exception { + allowUpdateOperationOutsideTransaction = "true"; + rebuildSessionFactory(); + prepareTest(); + + try(Session s = openSession(); + EntityManager entityManager = s.getEntityManagerFactory().createEntityManager();) { + + MyEntity entity = new MyEntity("N1"); + entityManager.persist(entity); + + QuerySqmImpl q = (QuerySqmImpl)entityManager.createNamedQuery("deleteByName"); + q.setParameter("name", "N1"); + int d = q.executeUpdate(); + assertEquals(0, d); + } + } + @Entity(name = "MyEntity") @Table(name = "MY_ENTITY") + @NamedQuery(name = "deleteByName", query = "delete from MyEntity where name = :name") public static class MyEntity { @Id @GeneratedValue From adbf8946da435aee7c82d2c7588afec442b44fdd Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 15 Mar 2024 17:42:33 +0100 Subject: [PATCH 191/321] HHH-17854 Avoid adding plural attribute restrictions multiple times --- .../ast/internal/LoaderSelectBuilder.java | 27 +--- .../AbstractCollectionPersister.java | 2 +- .../sqm/sql/BaseSqmToSqlAstConverter.java | 80 +----------- .../where/OneToManySQLRestrictionTests.java | 121 ++++++++++++++++++ 4 files changed, 127 insertions(+), 103 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/where/OneToManySQLRestrictionTests.java diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java index 3c9607e2afa5..e15ece98d927 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java @@ -689,6 +689,8 @@ private void applyFiltering( SqlAstCreationState astCreationState) { final NavigablePath parentNavigablePath = tableGroup.getNavigablePath().getParent(); if ( parentNavigablePath == null ) { + // Only apply restrictions for root table groups, + // because for table group joins the restriction is applied via PluralAttributeMappingImpl.createTableGroupJoin pluralAttributeMapping.applyBaseRestrictions( querySpec::applyPredicate, tableGroup, @@ -706,31 +708,6 @@ private void applyFiltering( astCreationState ); } - else { - final TableGroup parentTableGroup = astCreationState.getFromClauseAccess().getTableGroup( - parentNavigablePath ); - final TableGroupJoin pluralTableGroupJoin = parentTableGroup.findTableGroupJoin( tableGroup ); - assert pluralTableGroupJoin != null; - - final TableGroupJoin joinForPredicate = TableGroupJoinHelper.determineJoinForPredicateApply( pluralTableGroupJoin ); - - pluralAttributeMapping.applyBaseRestrictions( - joinForPredicate::applyPredicate, - tableGroup, - true, - loadQueryInfluencers.getEnabledFilters(), - null, - astCreationState - ); - pluralAttributeMapping.applyBaseManyToManyRestrictions( - joinForPredicate::applyPredicate, - tableGroup, - true, - loadQueryInfluencers.getEnabledFilters(), - null, - astCreationState - ); - } } private void applyFiltering( diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java index f5b3c321e99a..51f14764f065 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java @@ -293,7 +293,7 @@ public AbstractCollectionPersister( if ( StringHelper.isNotEmpty( collectionBootDescriptor.getWhere() ) ) { hasWhere = true; - sqlWhereString = "(" + collectionBootDescriptor.getWhere() + ") "; + sqlWhereString = "(" + collectionBootDescriptor.getWhere() + ")"; sqlWhereStringTemplate = Template.renderWhereStringTemplate( sqlWhereString, dialect, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index cb7558ee35b9..3a49a64e34d3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -481,7 +481,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base */ private Map>> trackedFetchSelectionsForGroup = Collections.emptyMap(); - private final Map collectionFilterPredicates = new IdentityHashMap<>(); private List> orderByFragments; private final SqlAliasBaseManager sqlAliasBaseManager = new SqlAliasBaseManager(); @@ -2080,7 +2079,6 @@ else if ( sqmQuerySpec.hasPositionalGroupItem() ) { ); orderByFragments = null; } - applyCollectionFilterPredicates( sqlQuerySpec ); } // Look for treated SqmFrom registrations that have uses of the untreated SqmFrom. @@ -2173,32 +2171,6 @@ private TableGroup findTableGroupByPath(NavigablePath navigablePath) { return getFromClauseAccess().getTableGroup( navigablePath ); } - protected void applyCollectionFilterPredicates(QuerySpec sqlQuerySpec) { - if ( CollectionHelper.isNotEmpty( collectionFilterPredicates ) ) { - final FromClauseAccess fromClauseAccess = getFromClauseAccess(); - OUTER: - for ( Map.Entry entry : collectionFilterPredicates.entrySet() ) { - final TableGroup parentTableGroup = fromClauseAccess.findTableGroup( entry.getKey().getParent() ); - if ( parentTableGroup == null ) { - // Since we only keep a single map, this could return null for collections of subqueries - continue; - } - for ( TableGroupJoin tableGroupJoin : parentTableGroup.getTableGroupJoins() ) { - if ( tableGroupJoin.getJoinedGroup().getNavigablePath() == entry.getKey() ) { - tableGroupJoin.applyPredicate( entry.getValue().getPredicate() ); - continue OUTER; - } - } - for ( TableGroupJoin tableGroupJoin : parentTableGroup.getNestedTableGroupJoins() ) { - if ( tableGroupJoin.getJoinedGroup().getNavigablePath() == entry.getKey() ) { - tableGroupJoin.applyPredicate( entry.getValue().getPredicate() ); - continue OUTER; - } - } - } - } - } - @Override public SelectClause visitSelectClause(SqmSelectClause selectClause) { currentClauseStack.push( Clause.SELECT ); @@ -8284,49 +8256,13 @@ public ImmutableFetchList visitNestedFetches(FetchParent fetchParent) { @Override public ImmutableFetchList visitFetches(FetchParent fetchParent) { - if ( fetchParent instanceof EagerCollectionFetch ) { + if ( fetchParent instanceof EagerCollectionFetch && currentQuerySpec().isRoot() ) { final EagerCollectionFetch collectionFetch = (EagerCollectionFetch) fetchParent; final PluralAttributeMapping pluralAttributeMapping = collectionFetch.getFetchedMapping(); final NavigablePath fetchablePath = collectionFetch.getNavigablePath(); - final TableGroup tableGroup = getFromClauseIndex().getTableGroup( fetchablePath ); - - // Base restrictions have already been applied if this is an explicit fetch - if ( getFromClauseIndex().findFetchedJoinByPath( fetchablePath ) == null ) { - final Restrictable restrictable = pluralAttributeMapping - .getCollectionDescriptor() - .getCollectionType() - .getAssociatedJoinable( getCreationContext().getSessionFactory() ); - restrictable.applyBaseRestrictions( - (predicate) -> addCollectionFilterPredicate( tableGroup.getNavigablePath(), predicate ), - tableGroup, - true, - getLoadQueryInfluencers().getEnabledFilters(), - null, - this - ); - } - - pluralAttributeMapping.applyBaseManyToManyRestrictions( - (predicate) -> { - final TableGroup parentTableGroup = getFromClauseIndex().getTableGroup( collectionFetch.getFetchParent().getNavigablePath() ); - final TableGroupJoin pluralTableGroupJoin = parentTableGroup.findTableGroupJoin( tableGroup ); - assert pluralTableGroupJoin != null; - - final TableGroupJoin joinForPredicate = TableGroupJoinHelper.determineJoinForPredicateApply( pluralTableGroupJoin ); - joinForPredicate.applyPredicate( predicate ); - }, - tableGroup, - true, - getLoadQueryInfluencers().getEnabledFilters(), - null, - this - ); - - if ( currentQuerySpec().isRoot() ) { - assert tableGroup.getModelPart() == pluralAttributeMapping; - applyOrdering( tableGroup, pluralAttributeMapping ); - } + assert tableGroup.getModelPart() == pluralAttributeMapping; + applyOrdering( tableGroup, pluralAttributeMapping ); } final FetchableContainer referencedMappingContainer = fetchParent.getReferencedMappingContainer(); final int keySize = referencedMappingContainer.getNumberOfKeyFetchables(); @@ -8404,16 +8340,6 @@ private Fetch buildFetch( } } - private void addCollectionFilterPredicate(NavigablePath navigablePath, Predicate predicate) { - final PredicateCollector existing = collectionFilterPredicates.get( navigablePath ); - if ( existing != null ) { - existing.applyPredicate( predicate ); - } - else { - collectionFilterPredicates.put( navigablePath, new PredicateCollector( predicate ) ); - } - } - private void applyOrdering(TableGroup tableGroup, PluralAttributeMapping pluralAttributeMapping) { if ( pluralAttributeMapping.getOrderByFragment() != null ) { applyOrdering( tableGroup, pluralAttributeMapping.getOrderByFragment() ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/where/OneToManySQLRestrictionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/where/OneToManySQLRestrictionTests.java new file mode 100644 index 000000000000..a37aec43ba9c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/where/OneToManySQLRestrictionTests.java @@ -0,0 +1,121 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html. + */ +package org.hibernate.orm.test.mapping.where; + +import java.time.LocalDateTime; +import java.util.HashSet; +import java.util.Set; + +import org.hibernate.annotations.SQLRestriction; +import org.hibernate.graph.spi.RootGraphImplementor; +import org.hibernate.jpa.AvailableHints; + +import org.hibernate.testing.jdbc.SQLStatementInspector; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; + +import static org.assertj.core.api.Assertions.assertThat; + +@DomainModel(annotatedClasses = { OneToManySQLRestrictionTests.Parent.class, OneToManySQLRestrictionTests.Child.class }) +@SessionFactory(useCollectingStatementInspector = true) +@Jira("https://hibernate.atlassian.net/browse/HHH-17854") +public class OneToManySQLRestrictionTests { + + @Test + public void testLoad(SessionFactoryScope scope) { + final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); + statementInspector.clear(); + + scope.inTransaction( (session) -> { + session.find( Parent.class, 1 ); + assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); + assertThat( statementInspector.getSqlQueries().get( 0 ) ) + .isEqualTo( + "select p1_0.id,cs1_0.parent_id,cs1_0.id,cs1_0.deleted_at " + + "from Parent p1_0 " + + "left join Child cs1_0 " + + "on p1_0.id=cs1_0.parent_id " + + "and (cs1_0.deleted_at IS NULL) " + + "where p1_0.id=?" + ); + } ); + } + + @Test + public void testLoad2(SessionFactoryScope scope) { + final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); + statementInspector.clear(); + + scope.inTransaction( (session) -> { + session.createQuery( "from Parent p join fetch p.childSet" ).getResultList(); + assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); + assertThat( statementInspector.getSqlQueries().get( 0 ) ) + .isEqualTo( + "select p1_0.id,cs1_0.parent_id,cs1_0.id,cs1_0.deleted_at " + + "from Parent p1_0 " + + "join Child cs1_0 " + + "on p1_0.id=cs1_0.parent_id " + + "and (cs1_0.deleted_at IS NULL)" + ); + } ); + } + + @Test + public void testLoad3(SessionFactoryScope scope) { + final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); + statementInspector.clear(); + + scope.inTransaction( (session) -> { + RootGraphImplementor entityGraph = session.createEntityGraph( Parent.class ); + entityGraph.addAttributeNode( "childSet" ); + session.createQuery( "from Parent p" ) + .setHint( AvailableHints.HINT_SPEC_LOAD_GRAPH, entityGraph ) + .getResultList(); + assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); + assertThat( statementInspector.getSqlQueries().get( 0 ) ) + .isEqualTo( + "select p1_0.id,cs1_0.parent_id,cs1_0.id,cs1_0.deleted_at " + + "from Parent p1_0 " + + "left join Child cs1_0 " + + "on p1_0.id=cs1_0.parent_id " + + "and (cs1_0.deleted_at IS NULL)" + ); + } ); + } + + @Entity(name = "Parent") + public static class Parent { + @Id + @GeneratedValue + private Long id; + @SQLRestriction("deleted_at IS NULL") + @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER) + private Set childSet = new HashSet<>(); + } + + @Entity(name = "Child") + public static class Child { + @Id + @GeneratedValue + private Long id; + @ManyToOne + private Parent parent; + @Column(name = "deleted_at") + private LocalDateTime deletedAt; + } +} From b9aa76625b4d7bfde984fb80d7c426d84ab53ae3 Mon Sep 17 00:00:00 2001 From: dgh Date: Wed, 28 Feb 2024 10:50:11 +0900 Subject: [PATCH 192/321] HHH-17759 Avoid need for undocumented escaping of colon characters in native queries --- .../query/sql/internal/ParameterParser.java | 25 ++++++++--- .../test/query/sql/ParameterParserTest.java | 45 ++++++++++++++++++- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterParser.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterParser.java index 67a7291ece8d..0a7f96b3129f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterParser.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterParser.java @@ -7,9 +7,10 @@ package org.hibernate.query.sql.internal; import java.util.BitSet; - +import java.util.Map; import org.hibernate.QueryException; import org.hibernate.QueryParameterException; +import org.hibernate.internal.log.DeprecationLogger; import org.hibernate.internal.util.StringHelper; import org.hibernate.query.ParameterLabelException; import org.hibernate.query.sql.spi.ParameterRecognizer; @@ -52,6 +53,7 @@ private ParameterParser() { */ public static void parse(String sqlString, ParameterRecognizer recognizer, boolean nativeJdbcParametersIgnored) throws QueryException { checkIsNotAFunctionCall( sqlString ); + sqlString = preprocessing(sqlString); final int stringLength = sqlString.length(); boolean inSingleQuotes = false; @@ -128,12 +130,8 @@ else if ( '\\' == c ) { } // otherwise else { - if ( c == ':' && indx < stringLength - 1 && sqlString.charAt( indx + 1 ) == ':') { - // colon character has been escaped - recognizer.other( c ); - indx++; - } - else if ( c == ':' ) { + if ( c == ':' && indx < stringLength - 1 && Character.isJavaIdentifierStart( sqlString.charAt( indx + 1 ) ) + && !(0 < indx && sqlString.charAt( indx - 1 ) == ':')) { // named parameter final int right = StringHelper.firstIndexOfChar( sqlString, HQL_SEPARATORS_BITSET, indx + 1 ); final int chopLocation = right < 0 ? sqlString.length() : right; @@ -178,6 +176,19 @@ else if ( c == '?' ) { recognizer.complete(); } + private static String preprocessing(String sqlString) { + final Map preprocessingExchangeMap = Map.of("::=", ":=", "::::", "::"); + for (Map.Entry entry : preprocessingExchangeMap.entrySet()) { + final String preprocessedSqlString = sqlString.replace(entry.getKey(), entry.getValue()); + if (!sqlString.equals(preprocessedSqlString)) { + DeprecationLogger.DEPRECATION_LOGGER.warn( + String.format("An unconventional syntax has been used in the SQL statement. It is recommended to use '%s' instead of '%s'.", entry.getValue(), entry.getKey())); + sqlString = preprocessedSqlString; + } + } + return sqlString; + } + public static void parse(String sqlString, ParameterRecognizer recognizer) throws QueryException { parse( sqlString, recognizer, false ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/ParameterParserTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/ParameterParserTest.java index 9c0c9a0d7f62..554d59f16f0e 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/ParameterParserTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/ParameterParserTest.java @@ -169,7 +169,50 @@ public void complete() { recognizer.complete(); assertEquals("SELECT @a,(@a:=20) FROM tbl_name", captured.toString()); } - + + @Test + @JiraKey( value = "HHH-17759") + public void testParseColonCharacterTypeCasting() { + final StringBuilder captured = new StringBuilder(); + ParameterRecognizer recognizer = new ParameterRecognizer() { + @Override + public void ordinalParameter(int position) { + // don't care + } + + @Override + public void namedParameter(String name, int position) { + // don't care + } + + @Override + public void jpaPositionalParameter(int name, int position) { + // don't care + } + + @Override + public void other(char character) { + captured.append(character); + } + + @Override + public void complete() { + } + + }; + String expectedQuery = "SELECT column_name::text FROM table_name"; + + ParameterParser.parse("SELECT column_name::text FROM table_name", recognizer); + recognizer.complete(); + assertEquals(expectedQuery, captured.toString()); + + captured.setLength(0); // clear for new test + + ParameterParser.parse("SELECT column_name::::text FROM table_name", recognizer); + recognizer.complete(); + assertEquals(expectedQuery, captured.toString()); + } + @Test public void testParseNamedParameter() { ExtendedParameterRecognizer recognizer = createRecognizer(); From 1d1278e58d5262bd039cfac1618bbbe2a3a7e64e Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Wed, 20 Mar 2024 18:25:56 +0100 Subject: [PATCH 193/321] HHH-17759 Avoid costly string search and replace --- .../internal/log/DeprecationLogger.java | 7 + .../query/sql/internal/ParameterParser.java | 72 ++++--- .../test/query/sql/ParameterParserTest.java | 178 +++++++++--------- 3 files changed, 145 insertions(+), 112 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/log/DeprecationLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/log/DeprecationLogger.java index e12d47315f4c..e970223ab8fa 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/log/DeprecationLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/log/DeprecationLogger.java @@ -283,4 +283,11 @@ void connectionProviderClassDeprecated( ) void deprecatedSettingNoReplacement(String settingName); + @LogMessage(level = WARN) + @Message( + id = 90000031, + value = "The native query colon escaping used for the [%s] operator is deprecated and will be removed. Use [%s] instead." + ) + void deprecatedNativeQueryColonEscaping(String oldOperator, String newOperator); + } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterParser.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterParser.java index 0a7f96b3129f..9b4b953252ef 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterParser.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterParser.java @@ -53,7 +53,6 @@ private ParameterParser() { */ public static void parse(String sqlString, ParameterRecognizer recognizer, boolean nativeJdbcParametersIgnored) throws QueryException { checkIsNotAFunctionCall( sqlString ); - sqlString = preprocessing(sqlString); final int stringLength = sqlString.length(); boolean inSingleQuotes = false; @@ -130,20 +129,52 @@ else if ( '\\' == c ) { } // otherwise else { - if ( c == ':' && indx < stringLength - 1 && Character.isJavaIdentifierStart( sqlString.charAt( indx + 1 ) ) - && !(0 < indx && sqlString.charAt( indx - 1 ) == ':')) { - // named parameter - final int right = StringHelper.firstIndexOfChar( sqlString, HQL_SEPARATORS_BITSET, indx + 1 ); - final int chopLocation = right < 0 ? sqlString.length() : right; - final String param = sqlString.substring( indx + 1, chopLocation ); - if ( param.isEmpty() ) { - throw new QueryParameterException( - "Space is not allowed after parameter prefix ':'", - sqlString - ); + if ( c == ':' ) { + if ( indx < stringLength - 1 && Character.isJavaIdentifierStart( sqlString.charAt( indx + 1 ) ) ) { + // named parameter + final int right = StringHelper.firstIndexOfChar( sqlString, HQL_SEPARATORS_BITSET, indx + 1 ); + final int chopLocation = right < 0 ? sqlString.length() : right; + final String param = sqlString.substring( indx + 1, chopLocation ); + if ( param.isEmpty() ) { + throw new QueryParameterException( + "Space is not allowed after parameter prefix ':'", + sqlString + ); + } + recognizer.namedParameter( param, indx ); + indx = chopLocation - 1; + } + else { + // For backwards compatibility, allow some known operators in the escaped form + if ( indx < stringLength - 3 + && sqlString.charAt( indx + 1 ) == ':' + && sqlString.charAt( indx + 2 ) == ':' + && sqlString.charAt( indx + 3 ) == ':' ) { + // Detect the :: operator, escaped as :::: + DeprecationLogger.DEPRECATION_LOGGER.deprecatedNativeQueryColonEscaping( "::::", "::" ); + recognizer.other( ':' ); + recognizer.other( ':' ); + indx += 3; + } + else if ( indx < stringLength - 2 + && sqlString.charAt( indx + 1 ) == ':' + && sqlString.charAt( indx + 2 ) == '=' ) { + // Detect the := operator, escaped as ::= + DeprecationLogger.DEPRECATION_LOGGER.deprecatedNativeQueryColonEscaping( "::=", ":=" ); + recognizer.other( ':' ); + recognizer.other( '=' ); + indx += 2; + } + else { + recognizer.other( ':' ); + // Consume all following colons as they are eagerly to not confuse named parameter detection + while ( indx < stringLength - 1 + && sqlString.charAt( indx + 1 ) == ':' ) { + indx++; + recognizer.other( ':' ); + } + } } - recognizer.namedParameter( param, indx ); - indx = chopLocation - 1; } else if ( c == '?' ) { // could be either a positional or JPA-style ordinal parameter @@ -176,19 +207,6 @@ else if ( c == '?' ) { recognizer.complete(); } - private static String preprocessing(String sqlString) { - final Map preprocessingExchangeMap = Map.of("::=", ":=", "::::", "::"); - for (Map.Entry entry : preprocessingExchangeMap.entrySet()) { - final String preprocessedSqlString = sqlString.replace(entry.getKey(), entry.getValue()); - if (!sqlString.equals(preprocessedSqlString)) { - DeprecationLogger.DEPRECATION_LOGGER.warn( - String.format("An unconventional syntax has been used in the SQL statement. It is recommended to use '%s' instead of '%s'.", entry.getValue(), entry.getKey())); - sqlString = preprocessedSqlString; - } - } - return sqlString; - } - public static void parse(String sqlString, ParameterRecognizer recognizer) throws QueryException { parse( sqlString, recognizer, false ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/ParameterParserTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/ParameterParserTest.java index 554d59f16f0e..8c4fdb3e3a03 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/ParameterParserTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/ParameterParserTest.java @@ -13,6 +13,7 @@ import org.hibernate.engine.query.internal.NativeQueryInterpreterStandardImpl; import org.hibernate.query.sql.internal.ParameterParser; import org.hibernate.query.sql.spi.ParameterRecognizer; + import org.hibernate.testing.orm.junit.JiraKey; import org.junit.jupiter.api.Test; @@ -73,7 +74,7 @@ public void testQuotedTextInComment() { recognizer.validate(); - assertTrue(recognizer.getNamedParameters().contains("param")); + assertTrue( recognizer.getNamedParameters().contains( "param" ) ); } @Test @@ -88,7 +89,7 @@ public void testContractionInComment() { recognizer.complete(); recognizer.validate(); - assertTrue( recognizer.getNamedParameters().contains("param")); + assertTrue( recognizer.getNamedParameters().contains( "param" ) ); } @Test @@ -133,94 +134,97 @@ public void testApostropheInOracleAlias() { recognizer.complete(); recognizer.validate(); - assertTrue(recognizer.getNamedParameters().contains("param")); + assertTrue( recognizer.getNamedParameters().contains( "param" ) ); } - - @Test - @JiraKey( value = "HHH-1237") - public void testParseColonCharacterEscaped() { - final StringBuilder captured = new StringBuilder(); - ParameterRecognizer recognizer = new ParameterRecognizer() { - @Override - public void ordinalParameter(int position) { - fail(); - } - - @Override - public void namedParameter(String name, int position) { - fail(); - } - - @Override - public void jpaPositionalParameter(int name, int position) { - fail(); - } - - @Override - public void other(char character) { - captured.append(character); - } + + @Test + @JiraKey(value = "HHH-1237") + public void testParseColonCharacterEscaped() { + final StringBuilder captured = new StringBuilder(); + ParameterRecognizer recognizer = new ParameterRecognizer() { + @Override + public void ordinalParameter(int position) { + fail(); + } + + @Override + public void namedParameter(String name, int position) { + fail(); + } + + @Override + public void jpaPositionalParameter(int name, int position) { + fail(); + } + + @Override + public void other(char character) { + captured.append( character ); + } @Override public void complete() { } }; - ParameterParser.parse("SELECT @a,(@a::=20) FROM tbl_name", recognizer); + ParameterParser.parse( "SELECT @a,(@a::=20) FROM tbl_name", recognizer ); recognizer.complete(); - assertEquals("SELECT @a,(@a:=20) FROM tbl_name", captured.toString()); - } + assertEquals( "SELECT @a,(@a:=20) FROM tbl_name", captured.toString() ); + } - @Test - @JiraKey( value = "HHH-17759") - public void testParseColonCharacterTypeCasting() { - final StringBuilder captured = new StringBuilder(); - ParameterRecognizer recognizer = new ParameterRecognizer() { - @Override - public void ordinalParameter(int position) { - // don't care - } - - @Override - public void namedParameter(String name, int position) { - // don't care - } - - @Override - public void jpaPositionalParameter(int name, int position) { - // don't care - } - - @Override - public void other(char character) { - captured.append(character); - } - - @Override - public void complete() { - } - - }; - String expectedQuery = "SELECT column_name::text FROM table_name"; - - ParameterParser.parse("SELECT column_name::text FROM table_name", recognizer); - recognizer.complete(); - assertEquals(expectedQuery, captured.toString()); - - captured.setLength(0); // clear for new test - - ParameterParser.parse("SELECT column_name::::text FROM table_name", recognizer); + @Test + @JiraKey(value = "HHH-17759") + public void testParseColonCharacterTypeCasting() { + final StringBuilder captured = new StringBuilder(); + ParameterRecognizer recognizer = new ParameterRecognizer() { + @Override + public void ordinalParameter(int position) { + // don't care + } + + @Override + public void namedParameter(String name, int position) { + // don't care + } + + @Override + public void jpaPositionalParameter(int name, int position) { + // don't care + } + + @Override + public void other(char character) { + captured.append( character ); + } + + @Override + public void complete() { + } + + }; + String expectedQuery = "SELECT column_name::text FROM table_name"; + + ParameterParser.parse( "SELECT column_name::text FROM table_name", recognizer ); recognizer.complete(); - assertEquals(expectedQuery, captured.toString()); - } + assertEquals( expectedQuery, captured.toString() ); - @Test - public void testParseNamedParameter() { - ExtendedParameterRecognizer recognizer = createRecognizer(); - NATIVE_QUERY_INTERPRETER.recognizeParameters("from Stock s where s.stockCode = :stockCode and s.xyz = :pxyz", recognizer); + captured.setLength( 0 ); // clear for new test + + ParameterParser.parse( "SELECT column_name::::text FROM table_name", recognizer ); + recognizer.complete(); + assertEquals( expectedQuery, captured.toString() ); + } + + @Test + public void testParseNamedParameter() { + ExtendedParameterRecognizer recognizer = createRecognizer(); + NATIVE_QUERY_INTERPRETER.recognizeParameters( + "from Stock s where s.stockCode = :stockCode and s.xyz = :pxyz", + recognizer + ); recognizer.complete(); recognizer.validate(); - assertTrue(recognizer.getNamedParameters().contains("stockCode")); + assertTrue(recognizer.getNamedParameters().contains("stockCode")); assertTrue(recognizer.getNamedParameters().contains("pxyz")); assertEquals( 2, recognizer.getNamedParameters().size() ); } @@ -232,15 +236,15 @@ public void testParseJPAPositionalParameter() { recognizer.complete(); recognizer.validate(); - assertEquals( 1, recognizer.getJpaPositionalParameterCount() ); + assertEquals( 1, recognizer.getJpaPositionalParameterCount() ); recognizer = createRecognizer(); - ParameterParser.parse("from Stock s where s.stockCode = ?1 and s.xyz = ?2", recognizer); + ParameterParser.parse( "from Stock s where s.stockCode = ?1 and s.xyz = ?2", recognizer ); recognizer.complete(); recognizer.validate(); - assertEquals( 2, recognizer.getJpaPositionalParameterCount() ); - } + assertEquals( 2, recognizer.getJpaPositionalParameterCount() ); + } @Test public void testJdbcParameterScanningEnabled() { @@ -278,7 +282,7 @@ public void testJdbcParameterScanningDisabled() { recognizer ); recognizer.validate(); - assertTrue(recognizer.getNamedParameters().contains("id")); + assertTrue( recognizer.getNamedParameters().contains( "id" ) ); assertEquals( 0, recognizer.getOrdinalParameterCount() ); } @@ -289,15 +293,18 @@ private ExtendedParameterRecognizer createRecognizer() { private interface ExtendedParameterRecognizer extends org.hibernate.query.sql.spi.ParameterRecognizer { void validate(); + int getOrdinalParameterCount(); + int getJpaPositionalParameterCount(); + Set getNamedParameters(); } private final static class TestParameterRecognizer implements ExtendedParameterRecognizer { private int ordinalParameterCount = 0; - private final Set jpaPositionalParameters = new HashSet<>(2); - private final Set namedParameters = new HashSet<>(2); + private final Set jpaPositionalParameters = new HashSet<>( 2 ); + private final Set namedParameters = new HashSet<>( 2 ); @Override public void ordinalParameter(int sourcePosition) { @@ -346,7 +353,8 @@ public Set getNamedParameters() { } private ParameterRecognitionException mixedParamStrategy() { - throw new ParameterRecognitionException( "Mixed parameter strategies - use just one of named, positional or JPA-ordinal strategy" ); + throw new ParameterRecognitionException( + "Mixed parameter strategies - use just one of named, positional or JPA-ordinal strategy" ); } } } From 288821accff5c6a8500ba72fd55362bf47aac217 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Tue, 20 Feb 2024 11:04:42 +0100 Subject: [PATCH 194/321] HHH-17872 Make sure that JdbcServicesImpl does not return a null SqlExceptionHelper --- .../jta/internal/JtaIsolationDelegate.java | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaIsolationDelegate.java b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaIsolationDelegate.java index 5870b2de4039..2e3ec1553362 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaIsolationDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaIsolationDelegate.java @@ -13,10 +13,15 @@ import java.sql.Connection; import java.sql.SQLException; import java.util.concurrent.Callable; +import java.util.function.BiFunction; +import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; +import org.hibernate.JDBCException; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; +import org.hibernate.exception.internal.SQLStateConversionDelegate; +import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.resource.jdbc.spi.JdbcSessionOwner; import org.hibernate.resource.transaction.spi.IsolationDelegate; import org.hibernate.internal.CoreLogging; @@ -35,7 +40,7 @@ public class JtaIsolationDelegate implements IsolationDelegate { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( JtaIsolationDelegate.class ); private final JdbcConnectionAccess connectionAccess; - private final SqlExceptionHelper sqlExceptionHelper; + private final BiFunction sqlExceptionConverter; private final TransactionManager transactionManager; public JtaIsolationDelegate(TransactionCoordinatorOwner transactionCoordinatorOwner, TransactionManager transactionManager) { @@ -52,19 +57,30 @@ public JtaIsolationDelegate(JdbcSessionOwner jdbcSessionOwner, TransactionManage public JtaIsolationDelegate( JdbcConnectionAccess connectionAccess, - SqlExceptionHelper sqlExceptionHelper, + SqlExceptionHelper sqlExceptionConverter, TransactionManager transactionManager) { this.connectionAccess = connectionAccess; - this.sqlExceptionHelper = sqlExceptionHelper; this.transactionManager = transactionManager; + if ( sqlExceptionConverter != null ) { + this.sqlExceptionConverter = sqlExceptionConverter::convert; + } + else { + SQLStateConversionDelegate delegate = new SQLStateConversionDelegate( + () -> { + throw new AssertionFailure( + "Unexpected call to ConversionContext.getViolatedConstraintNameExtractor" ); + } + ); + this.sqlExceptionConverter = (sqlException, message) -> delegate.convert( sqlException, message, null ); + } } - protected JdbcConnectionAccess jdbcConnectionAccess() { + private JdbcConnectionAccess jdbcConnectionAccess() { return connectionAccess; } - protected SqlExceptionHelper sqlExceptionHelper() { - return sqlExceptionHelper; + private BiFunction sqlExceptionConverter() { + return sqlExceptionConverter; } @Override @@ -183,7 +199,7 @@ private T doTheWork(WorkExecutorVisitable work) { } } catch (SQLException e) { - throw sqlExceptionHelper().convert( e, "unable to obtain isolated JDBC connection" ); + throw sqlExceptionConverter().apply( e, "unable to obtain isolated JDBC connection" ); } } From c47126205f5fde9ae5fc6b2b37255de54856d2fb Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Mon, 18 Mar 2024 19:07:54 +0100 Subject: [PATCH 195/321] HHH-17801 Resolve enum basic types to avoid type validation issues --- .../internal/InferredBasicValueResolver.java | 5 ++++- .../test/function/array/ArrayPositionTest.java | 11 +++++++++++ .../orm/test/function/array/EntityWithArrays.java | 15 +++++++++++++++ .../hibernate/orm/test/function/array/Label.java | 14 ++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/function/array/Label.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java index d30a48c4d263..20cd61c62497 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java @@ -295,7 +295,10 @@ public static > BasicValue.Resolution fromEnum( final JdbcType jdbcType = explicitJdbcType == null ? enumJavaType.getRecommendedJdbcType( stdIndicators ) : explicitJdbcType; - final BasicTypeImpl basicType = new BasicTypeImpl<>( enumJavaType, jdbcType ); + final BasicType basicType = bootstrapContext.getTypeConfiguration().getBasicTypeRegistry().resolve( + enumJavaType, + jdbcType + ); bootstrapContext.registerAdHocBasicType( basicType ); return new InferredBasicValueResolution<>( basicType, diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/ArrayPositionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/ArrayPositionTest.java index 26a8a976450c..be357b8f0768 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/ArrayPositionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/ArrayPositionTest.java @@ -8,6 +8,7 @@ import java.util.Collection; import java.util.List; +import java.util.Set; import org.hibernate.query.criteria.JpaCriteriaQuery; import org.hibernate.query.criteria.JpaRoot; @@ -17,6 +18,7 @@ import org.hibernate.testing.orm.junit.BootstrapServiceRegistry; import org.hibernate.testing.orm.junit.DialectFeatureChecks; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; @@ -85,6 +87,15 @@ public void testPositionNull(SessionFactoryScope scope) { } ); } + @Test + @Jira("https://hibernate.atlassian.net/browse/HHH-17801") + public void testEnumPosition(SessionFactoryScope scope) { + scope.inSession( em -> { + em.createQuery( "from EntityWithArrays e where array_position(e.theLabels, e.theLabel) > 0", EntityWithArrays.class ) + .getResultList(); + } ); + } + @Test public void testNodeBuilderArray(SessionFactoryScope scope) { scope.inSession( em -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/EntityWithArrays.java b/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/EntityWithArrays.java index 350baa9c91af..c03c1e76f3d0 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/EntityWithArrays.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/EntityWithArrays.java @@ -8,6 +8,7 @@ package org.hibernate.orm.test.function.array; import java.util.List; +import java.util.Set; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -24,6 +25,12 @@ public class EntityWithArrays { @Column(name = "the_array", insertable = false, updatable = false) private List theCollection; + @Column(name = "the_label") + private Label theLabel; + + @Column(name = "the_labels") + private Set