From 87441781c61ef527c60be99ce8750a0d4699c1f8 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Fri, 8 Nov 2019 17:05:09 +0000 Subject: [PATCH 01/23] upgrade gradle-bintray-plugin to 1.8.4 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 08e78d204d..e65e9616b4 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ buildscript { classpath 'org.hibernate.build.gradle:gradle-xjc-plugin:1.0.2.Final' classpath 'gradle.plugin.com.github.lburgazzoli:gradle-karaf-plugin:0.1.1' classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.7' - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.3' + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' classpath 'de.thetaphi:forbiddenapis:2.5' } } From 66515a2e4e9ac312b58575fdc6dcdce9dacc27ea Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Mon, 4 Nov 2019 12:09:00 +0000 Subject: [PATCH 02/23] HHH-13723 Hint sizing of ArrayList in ResultSetProcessingContextImpl --- .../exec/process/internal/ResultSetProcessingContextImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java index 791132e67d..8b9cfe8486 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java @@ -281,8 +281,9 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex // managing the running list of registrations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + final int sizeHint = currentRowHydratedEntityRegistrationList.size(); if ( hydratedEntityRegistrationList == null ) { - hydratedEntityRegistrationList = new ArrayList<>(); + hydratedEntityRegistrationList = new ArrayList<>( sizeHint ); } hydratedEntityRegistrationList.addAll( currentRowHydratedEntityRegistrationList ); From 164e1fc7cc2d79c0ce2e309ae1a486ddf2607c5f Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Mon, 11 Nov 2019 19:05:25 +0000 Subject: [PATCH 03/23] HHH-13687 TenantSchemaResolver not called in integration test after upgrade from --- .../internal/SessionFactoryImpl.java | 36 ++++++++++++++----- .../DiscriminatorMultiTenancyTest.java | 7 ++-- .../testing/transaction/TransactionUtil.java | 24 +++++++++++++ 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java index c0176cffe1..3baeba0b7d 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -380,10 +380,7 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { } this.defaultSessionOpenOptions = withOptions(); - this.temporarySessionOpenOptions = withOptions() - .autoClose( false ) - .flushMode( FlushMode.MANUAL ) - .connectionHandlingMode( PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT ); + this.temporarySessionOpenOptions = buildTemporarySessionOpenOptions(); this.fastSessionServices = new FastSessionServices( this ); this.observer.sessionFactoryCreated( this ); @@ -406,6 +403,13 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { } } + private SessionBuilder buildTemporarySessionOpenOptions() { + return withOptions() + .autoClose( false ) + .flushMode( FlushMode.MANUAL ) + .connectionHandlingMode( PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT ); + } + private void prepareEventListeners(MetadataImplementor metadata) { final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class ); final ConfigurationService cfgService = serviceRegistry.getService( ConfigurationService.class ); @@ -481,11 +485,26 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { } public Session openSession() throws HibernateException { - return this.defaultSessionOpenOptions.openSession(); + final CurrentTenantIdentifierResolver currentTenantIdentifierResolver = getCurrentTenantIdentifierResolver(); + //We can only use reuse the defaultSessionOpenOptions as a constant when there is no TenantIdentifierResolver + if ( currentTenantIdentifierResolver != null ) { + return this.withOptions().openSession(); + } + else { + return this.defaultSessionOpenOptions.openSession(); + } } public Session openTemporarySession() throws HibernateException { - return this.temporarySessionOpenOptions.openSession(); + final CurrentTenantIdentifierResolver currentTenantIdentifierResolver = getCurrentTenantIdentifierResolver(); + //We can only use reuse the defaultSessionOpenOptions as a constant when there is no TenantIdentifierResolver + if ( currentTenantIdentifierResolver != null ) { + return buildTemporarySessionOpenOptions() + .openSession(); + } + else { + return this.temporarySessionOpenOptions.openSession(); + } } public Session getCurrentSession() throws HibernateException { @@ -1423,8 +1442,9 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { public StatelessSessionBuilderImpl(SessionFactoryImpl sessionFactory) { this.sessionFactory = sessionFactory; - if ( sessionFactory.getCurrentTenantIdentifierResolver() != null ) { - tenantIdentifier = sessionFactory.getCurrentTenantIdentifierResolver().resolveCurrentTenantIdentifier(); + CurrentTenantIdentifierResolver tenantIdentifierResolver = sessionFactory.getCurrentTenantIdentifierResolver(); + if ( tenantIdentifierResolver != null ) { + tenantIdentifier = tenantIdentifierResolver.resolveCurrentTenantIdentifier(); } queryParametersValidationEnabled = sessionFactory.getSessionFactoryOptions().isQueryParametersValidationEnabled(); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/multitenancy/discriminator/DiscriminatorMultiTenancyTest.java b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/discriminator/DiscriminatorMultiTenancyTest.java index d9d22b9391..f15e825257 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/multitenancy/discriminator/DiscriminatorMultiTenancyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/discriminator/DiscriminatorMultiTenancyTest.java @@ -193,9 +193,10 @@ public class DiscriminatorMultiTenancyTest extends BaseUnitTestCase { } } - public void doInHibernate(String tenant, - Consumer function) { + public void doInHibernate(String tenant, Consumer function) { currentTenantResolver.currentTenantIdentifier = tenant; - TransactionUtil.doInHibernate( this::sessionFactory, tenant, function); + //Careful: do not use the #doInHibernate version of the method which takes a tenant: the goal of these tests is + // to verify that the CurrentTenantIdentifierResolver is being applied! + TransactionUtil.doInHibernate( this::sessionFactory, function); } } \ No newline at end of file diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java b/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java index 67de5eb55c..541640390b 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java @@ -35,6 +35,8 @@ import org.hibernate.dialect.SQLServerDialect; import org.hibernate.dialect.SybaseASE15Dialect; import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.junit.Assert; + import org.jboss.logging.Logger; /** @@ -44,6 +46,28 @@ public class TransactionUtil { private static final Logger log = Logger.getLogger( TransactionUtil.class ); + public static void doInHibernate(Supplier factorySupplier, Consumer function) { + final SessionFactory sessionFactory = factorySupplier.get(); + Assert.assertNotNull( "SessionFactory is null in test!", sessionFactory ); + //Make sure any error is propagated + try ( Session session = sessionFactory.openSession() ) { + final Transaction txn = session.getTransaction(); + txn.begin(); + try { + function.accept( session ); + } + catch (Throwable e) { + try { + txn.rollback(); + } + finally { + throw e; + } + } + txn.commit(); + } + } + /** * Hibernate transaction function * From 05e6a41e5fc13cc76dd8f42b8b665fdbf775b2d3 Mon Sep 17 00:00:00 2001 From: Jan-Willem Gmelig Meyling Date: Thu, 31 Oct 2019 18:42:58 +0100 Subject: [PATCH 04/23] HHH-13670 - Reproducer Missing from clause in query with joined inheritance, regression in 5.4.5 --- .../query/hhh13670/HHH13670Test.java | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/query/hhh13670/HHH13670Test.java diff --git a/hibernate-core/src/test/java/org/hibernate/query/hhh13670/HHH13670Test.java b/hibernate-core/src/test/java/org/hibernate/query/hhh13670/HHH13670Test.java new file mode 100644 index 0000000000..701de49b1d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/query/hhh13670/HHH13670Test.java @@ -0,0 +1,146 @@ +/* + * 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.query.hhh13670; + +import org.hibernate.cfg.Configuration; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Before; +import org.junit.Test; + +import javax.persistence.Column; +import javax.persistence.ConstraintMode; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.ForeignKey; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Tuple; +import java.util.List; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +@TestForIssue(jiraKey = "HHH-13670") +public class HHH13670Test extends BaseCoreFunctionalTestCase { + + @Before + public void setUp() { + doInJPA(this::sessionFactory, em -> { + SubA a_1 = new SubA(1L); + SubA a_2 = new SubA(2L); + SubA a_3 = new SubA(3L); + SubA a_14 = em.getReference(SubA.class, 10L); + SubB b_4 = new SubB(4L, null); + SubB b_5 = new SubB(5L, a_3); + SubB b_6 = new SubB(6L, b_4); + SubB b_7 = new SubB(7L, a_14); + + em.merge(a_1); + em.merge(a_2); + em.merge(a_3); + em.merge(b_4); + em.merge(b_5); + em.merge(b_6); + em.merge(b_7); + }); + } + + @Test + public void testRootTypeJoinWithGroupJoins() { + doInJPA(this::sessionFactory, em -> { + List resultList = em.createQuery("SELECT subB_0.id, subA_0.id, subB_0.id, subA_0.id FROM SubB subB_0 LEFT JOIN Super subA_0 ON subA_0.id = subB_0.parent.id ORDER BY subB_0.id ASC, subA_0.id ASC", Tuple.class) + .getResultList(); + + assertEquals("Rows omitted despite optional association should have rendered a left join", 4, resultList.size()); + + assertEquals((Long) 4L , resultList.get(0).get(0)); + assertEquals((Long) 5L , resultList.get(1).get(0)); + assertEquals((Long) 6L , resultList.get(2).get(0)); + assertEquals((Long) 7L , resultList.get(3).get(0)); + + assertNull(resultList.get(0).get(1, Long.class)); + assertEquals((Long) 3L , resultList.get(1).get(1, Long.class)); + assertEquals((Long) 4L , resultList.get(2).get(1, Long.class)); + assertNull("Missing entry in foreign table should not be returned", resultList.get(3).get(1, Long.class)); + }); + } + + @Test + public void testSubTypeJoinWithTableGroupJoins() { + doInJPA(this::sessionFactory, em -> { + List resultList = em.createQuery("SELECT subB_0.id, subA_0.id, subB_0.id, subA_0.id FROM SubB subB_0 LEFT JOIN SubA subA_0 ON subA_0.id = subB_0.parent.id ORDER BY subB_0.id ASC, subA_0.id ASC", Tuple.class) + .getResultList(); + + assertEquals("Rows omitted despite optional association should have rendered a left join", 4, resultList.size()); + + assertEquals((Long) 4L, resultList.get(0).get(0)); + assertEquals((Long) 5L, resultList.get(1).get(0)); + assertEquals((Long) 6L, resultList.get(2).get(0)); + assertEquals((Long) 7L, resultList.get(3).get(0)); + + assertNull(resultList.get(0).get(1, Long.class)); + assertEquals((Long) 3L, resultList.get(1).get(1, Long.class)); + assertNull("Another subtype than queried for was returned", resultList.get(2).get(1)); + assertNull("Missing entry in foreign table should not be returned", resultList.get(3).get(1, Long.class)); + }); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Super.class, SubA.class, SubB.class }; + } + + @Override + protected void configure(Configuration configuration) { + super.afterConfigurationBuilt( configuration ); + // Uncomment to fix tests +// configuration.setProperty( AvailableSettings.OMIT_JOIN_OF_SUPERCLASS_TABLES, "false" ); + } + + @Entity(name = "Super") + @Inheritance(strategy = InheritanceType.JOINED) + public static class Super { + + @Id + @Column + Long id; + + @JoinColumn(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)) + @ManyToOne(targetEntity = Super.class, fetch = FetchType.LAZY) + SubType parent; + + } + + @Entity(name = "SubA") + public static class SubA extends Super { + + SubA() {} + + SubA(Long id) { + this.id = id; + } + + } + + @Entity(name = "SubB") + public static class SubB extends Super { + + SubB() {} + + SubB(Long id, Super parent) { + this.id = id; + ((Super) this).parent = parent; + } + + } + +} From 0c0248d448a9316623bc0cd3246fec829e5e3f1c Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Sat, 2 Nov 2019 15:37:21 +0100 Subject: [PATCH 05/23] Include the WITH clause AST in the FromElement so that column references can be analyzed --- .../hql/internal/ast/HqlSqlWalker.java | 2 +- .../hql/internal/ast/tree/FromElement.java | 9 +++- .../hql/internal/ast/util/JoinProcessor.java | 46 +++++++++---------- .../query/hhh13670/HHH13670Test.java | 17 ++++--- 4 files changed, 40 insertions(+), 34 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlSqlWalker.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlSqlWalker.java index f493f9c974..eff9592a8f 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlSqlWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlSqlWalker.java @@ -509,7 +509,7 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par SqlGenerator sql = new SqlGenerator( getSessionFactoryHelper().getFactory() ); sql.whereExpr( hqlSqlWithNode.getFirstChild() ); - fromElement.setWithClauseFragment( "(" + sql.getSQL() + ")" ); + fromElement.setWithClauseFragment( hqlSqlWithNode.getFirstChild(), "(" + sql.getSQL() + ")" ); } catch (SemanticException e) { throw e; diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java index 6470c33d99..7c0db88a37 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import antlr.collections.AST; import org.hibernate.QueryException; import org.hibernate.engine.internal.JoinSequence; import org.hibernate.hql.internal.CollectionProperties; @@ -69,6 +70,7 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa private boolean useWhereFragment = true; private List destinations; private boolean manyToMany; + private AST withClauseAst; private String withClauseFragment; private boolean dereferencedBySuperclassProperty; private boolean dereferencedBySubclassProperty; @@ -627,11 +629,16 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa isAllPropertyFetch = fetch; } + public AST getWithClauseAst() { + return withClauseAst; + } + public String getWithClauseFragment() { return withClauseFragment; } - public void setWithClauseFragment(String withClauseFragment) { + public void setWithClauseFragment(AST ast, String withClauseFragment) { + this.withClauseAst = ast; this.withClauseFragment = withClauseFragment; } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java index f3361a8e5e..3ab21e1767 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java @@ -103,24 +103,6 @@ public class JoinProcessor implements SqlTokenTypes { } } - private List findAllNodes(AST node, Class clazz) { - ArrayList found = new ArrayList<>(); - doFindAllNodes( node, clazz, found ); - return found; - } - - private void doFindAllNodes(AST node, Class clazz, List found) { - if ( clazz.isAssignableFrom( node.getClass() ) ) { - found.add( (T) node ); - } - if ( node.getFirstChild() != null ) { - doFindAllNodes( node.getFirstChild(), clazz, found ); - } - if ( node.getNextSibling() != null ) { - doFindAllNodes( node.getNextSibling(), clazz, found ); - } - } - private Set findQueryReferencedTables(QueryNode query) { if ( !walker.getSessionFactoryHelper() .getFactory() @@ -150,16 +132,15 @@ public class JoinProcessor implements SqlTokenTypes { Set result = new HashSet<>(); // Find tables referenced by FromReferenceNodes - List fromReferenceNodes = findAllNodes( query, FromReferenceNode.class ); - for ( FromReferenceNode node : fromReferenceNodes ) { - String[] tables = node.getReferencedTables(); - if ( tables != null ) { - for ( String table : tables ) { - result.add( table ); - } + collectReferencedTables( new ASTIterator( query ), result ); + for (FromElement fromElement : (List) query.getFromClause().getFromElements()) { + AST withClauseAst = fromElement.getWithClauseAst(); + if ( withClauseAst != null ) { + collectReferencedTables( new ASTIterator( withClauseAst ), result ); } } + // Find tables referenced by fromElementsForLoad if ( query.getSelectClause() != null ) { for ( Object element : query.getSelectClause().getFromElementsForLoad() ) { @@ -178,6 +159,21 @@ public class JoinProcessor implements SqlTokenTypes { return result; } + private void collectReferencedTables(ASTIterator iterator, Set result) { + while ( iterator.hasNext() ) { + AST node = iterator.nextNode(); + if ( node instanceof FromReferenceNode ) { + FromReferenceNode fromReferenceNode = (FromReferenceNode) node; + String[] tables = fromReferenceNode.getReferencedTables(); + if ( tables != null ) { + for ( String table : tables ) { + result.add( table ); + } + } + } + } + } + public void processJoins(QueryNode query) { final FromClause fromClause = query.getFromClause(); diff --git a/hibernate-core/src/test/java/org/hibernate/query/hhh13670/HHH13670Test.java b/hibernate-core/src/test/java/org/hibernate/query/hhh13670/HHH13670Test.java index 701de49b1d..ff3cda4bc7 100644 --- a/hibernate-core/src/test/java/org/hibernate/query/hhh13670/HHH13670Test.java +++ b/hibernate-core/src/test/java/org/hibernate/query/hhh13670/HHH13670Test.java @@ -54,6 +54,13 @@ public class HHH13670Test extends BaseCoreFunctionalTestCase { }); } + @Test + public void testDereferenceSuperClassAttributeInWithClause() { + doInJPA(this::sessionFactory, em -> { + em.createQuery("SELECT subB_0.id FROM SubB subB_0 LEFT JOIN subB_0.other subA_0 ON subA_0.id = subB_0.parent.id", Tuple.class).getResultList(); + }); + } + @Test public void testRootTypeJoinWithGroupJoins() { doInJPA(this::sessionFactory, em -> { @@ -99,13 +106,6 @@ public class HHH13670Test extends BaseCoreFunctionalTestCase { return new Class[] { Super.class, SubA.class, SubB.class }; } - @Override - protected void configure(Configuration configuration) { - super.afterConfigurationBuilt( configuration ); - // Uncomment to fix tests -// configuration.setProperty( AvailableSettings.OMIT_JOIN_OF_SUPERCLASS_TABLES, "false" ); - } - @Entity(name = "Super") @Inheritance(strategy = InheritanceType.JOINED) public static class Super { @@ -134,6 +134,9 @@ public class HHH13670Test extends BaseCoreFunctionalTestCase { @Entity(name = "SubB") public static class SubB extends Super { + @ManyToOne(fetch = FetchType.LAZY) + Super other; + SubB() {} SubB(Long id, Super parent) { From 9ddab37748de6c313e93ac98e7d720bf0c880c34 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 13 Nov 2019 09:08:12 +0000 Subject: [PATCH 06/23] HHH-13727 H2 database with DATABASE_TO_UPPER=false throws org.h2.jdbc.JdbcSQLSyntaxErrorException: Table sequences not found --- .../src/main/java/org/hibernate/dialect/H2Dialect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b16be8648b..06468ef8ee 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -102,7 +102,7 @@ public class H2Dialect extends Dialect { if ( buildId >= 32 ) { this.sequenceInformationExtractor = SequenceInformationExtractorH2DatabaseImpl.INSTANCE; - this.querySequenceString = "select * from INFORMATION_SCHEMA.sequences"; + this.querySequenceString = "select * from INFORMATION_SCHEMA.SEQUENCES"; } else { this.sequenceInformationExtractor = SequenceInformationExtractorNoOpImpl.INSTANCE; From 19692c13018478b3bde7de3bd74adf2a8310dc90 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Thu, 14 Nov 2019 08:45:23 +0000 Subject: [PATCH 07/23] HHH-13730 Upgrade to Classmate 1.4.0 --- gradle/libraries.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libraries.gradle b/gradle/libraries.gradle index b8152c62ed..b656b68aa1 100644 --- a/gradle/libraries.gradle +++ b/gradle/libraries.gradle @@ -53,7 +53,7 @@ ext { // Annotations commons_annotations: "org.hibernate.common:hibernate-commons-annotations:${hibernateCommonsVersion}", jandex: 'org.jboss:jandex:2.0.5.Final', - classmate: 'com.fasterxml:classmate:1.3.4', + classmate: 'com.fasterxml:classmate:1.4.0', // Dom4J dom4j: 'org.dom4j:dom4j:2.1.1@jar', From fcb8fdde3e7ae74029a88a1dca4369d2a132c088 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Thu, 14 Nov 2019 10:10:48 +0000 Subject: [PATCH 08/23] HHH-13731 Upgrade to Classmate 1.5.1 --- gradle/libraries.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libraries.gradle b/gradle/libraries.gradle index b656b68aa1..084eb9fe9c 100644 --- a/gradle/libraries.gradle +++ b/gradle/libraries.gradle @@ -53,7 +53,7 @@ ext { // Annotations commons_annotations: "org.hibernate.common:hibernate-commons-annotations:${hibernateCommonsVersion}", jandex: 'org.jboss:jandex:2.0.5.Final', - classmate: 'com.fasterxml:classmate:1.4.0', + classmate: 'com.fasterxml:classmate:1.5.1', // Dom4J dom4j: 'org.dom4j:dom4j:2.1.1@jar', From f7c8ba853269e3ea39923ea969d9d2b0be1ea693 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Thu, 14 Nov 2019 10:11:22 +0000 Subject: [PATCH 09/23] HHH-13733 Upgrade to Jandex 2.1.1.Final --- gradle/libraries.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libraries.gradle b/gradle/libraries.gradle index 084eb9fe9c..799866b9c8 100644 --- a/gradle/libraries.gradle +++ b/gradle/libraries.gradle @@ -52,7 +52,7 @@ ext { // Annotations commons_annotations: "org.hibernate.common:hibernate-commons-annotations:${hibernateCommonsVersion}", - jandex: 'org.jboss:jandex:2.0.5.Final', + jandex: 'org.jboss:jandex:2.1.1.Final', classmate: 'com.fasterxml:classmate:1.5.1', // Dom4J From 47c8a89390aa4ea36a8779fc6139b4ff2eab6b7d Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Thu, 14 Nov 2019 11:13:58 +0100 Subject: [PATCH 10/23] HHH-13712 - Test and fix for missing superclass table joins when joining superclass associations --- .../hql/internal/ast/util/JoinProcessor.java | 5 + .../query/hhh13712/HHH13712Test.java | 122 ++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/query/hhh13712/HHH13712Test.java diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java index 3ab21e1767..93e5f85721 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java @@ -134,6 +134,11 @@ public class JoinProcessor implements SqlTokenTypes { // Find tables referenced by FromReferenceNodes collectReferencedTables( new ASTIterator( query ), result ); for (FromElement fromElement : (List) query.getFromClause().getFromElements()) { + // For joins, we want to add the table where the association key is mapped as well as that could be a supertype that we need to join + String role = fromElement.getRole(); + if ( role != null ) { + result.add( fromElement.getOrigin().getPropertyTableName(role.substring(role.lastIndexOf('.') + 1)) ); + } AST withClauseAst = fromElement.getWithClauseAst(); if ( withClauseAst != null ) { collectReferencedTables( new ASTIterator( withClauseAst ), result ); diff --git a/hibernate-core/src/test/java/org/hibernate/query/hhh13712/HHH13712Test.java b/hibernate-core/src/test/java/org/hibernate/query/hhh13712/HHH13712Test.java new file mode 100644 index 0000000000..0591966ea0 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/query/hhh13712/HHH13712Test.java @@ -0,0 +1,122 @@ +/* + * 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.query.hhh13712; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Before; +import org.junit.Test; + +import javax.persistence.Column; +import javax.persistence.ConstraintMode; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.ForeignKey; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Tuple; +import java.util.List; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +@TestForIssue(jiraKey = "HHH-13712") +public class HHH13712Test extends BaseCoreFunctionalTestCase { + + @Before + public void setUp() { + doInJPA(this::sessionFactory, em -> { + SomeOther a_1 = new SomeOther(1L); + SomeOther a_2 = new SomeOther(2L); + SomeOther a_3 = new SomeOther(3L); + SubObject b_5 = new SubObject(5L, a_1); + SubObject b_6 = new SubObject(6L, a_2); + SubObject b_7 = new SubObject(7L, a_3); + + em.merge(a_1); + em.merge(a_2); + em.merge(a_3); + em.merge(b_5); + em.merge(b_6); + em.merge(b_7); + }); + } + + @Test + public void testJoinSuperclassAssociationOnly() { + doInJPA(this::sessionFactory, em -> { + List actual = em.createQuery("SELECT 1 FROM SubObject sub LEFT JOIN sub.parent p", Integer.class).getResultList(); + assertEquals(3, actual.size()); + }); + } + + @Test + public void testJoinSuperclassAssociation() { + doInJPA(this::sessionFactory, em -> { + long actual = em.createQuery("SELECT COUNT(sub) FROM SubObject sub LEFT JOIN sub.parent p WHERE p.id = 1", Long.class).getSingleResult(); + assertEquals(1L, actual); + }); + } + + @Test + public void testCountParentIds() { + doInJPA(this::sessionFactory, em -> { + long actual = em.createQuery("SELECT COUNT(distinct sub.parent.id) FROM SubObject sub", Long.class).getSingleResult(); + assertEquals(3L, actual); + }); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Super.class, SubObject.class, SomeOther.class }; + } + + @Entity(name = "Super") + @Inheritance(strategy = InheritanceType.JOINED) + public static class Super { + + @Id + @Column + Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(nullable = false) + SomeOther parent; + + } + + @Entity(name = "SubObject") + public static class SubObject extends Super { + + SubObject() {} + + SubObject(Long id, SomeOther parent) { + this.id = id; + this.parent = parent; + } + + } + + @Entity(name = "SomeOther") + public static class SomeOther { + + @Id + @Column + Long id; + + SomeOther() {} + + SomeOther(Long id) { + this.id = id; + } + } + +} From 842a15561564a5ec5af4018286d44d1253ef82b0 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 28 Oct 2019 14:21:23 +0000 Subject: [PATCH 11/23] HHH-13705 Add test for issue HHH-13705 Add test for issue --- .../DirtyCheckEnhancementContext.java | 21 + .../ManyToOnePropertyAccessByFieldTest.java | 691 ++++++++++++++++++ ...neWithEmbeddedAndNotOptionalFieldTest.java | 195 +++++ .../NoDirtyCheckEnhancementContext.java | 26 + 4 files changed, 933 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/DirtyCheckEnhancementContext.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/ManyToOnePropertyAccessByFieldTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/ManyToOneWithEmbeddedAndNotOptionalFieldTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/NoDirtyCheckEnhancementContext.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/DirtyCheckEnhancementContext.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/DirtyCheckEnhancementContext.java new file mode 100644 index 0000000000..53f5afdf2c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/DirtyCheckEnhancementContext.java @@ -0,0 +1,21 @@ +/* + * 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.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking; + +import org.hibernate.bytecode.enhance.spi.UnloadedClass; + +import org.hibernate.testing.bytecode.enhancement.EnhancerTestContext; + +/** + * @author Andrea Boriero + */ +public class DirtyCheckEnhancementContext extends EnhancerTestContext { + @Override + public boolean doExtendedEnhancement(UnloadedClass classDescriptor) { + return false; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/ManyToOnePropertyAccessByFieldTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/ManyToOnePropertyAccessByFieldTest.java new file mode 100644 index 0000000000..25b90e82c0 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/ManyToOnePropertyAccessByFieldTest.java @@ -0,0 +1,691 @@ +/* + * 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.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking; + +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.persistence.Cacheable; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.DiscriminatorColumn; +import javax.persistence.DiscriminatorType; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Embeddable; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +import org.hibernate.annotations.NaturalId; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.SessionFactoryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.stat.Statistics; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author Andrea Boriero + */ +@RunWith(BytecodeEnhancerRunner.class) +@CustomEnhancementContext({ DirtyCheckEnhancementContext.class, NoDirtyCheckEnhancementContext.class }) +@TestForIssue(jiraKey = "HHH-13705") +public class ManyToOnePropertyAccessByFieldTest extends BaseNonConfigCoreFunctionalTestCase { + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" ); + ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" ); + ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" ); + } + + @Override + protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) { + super.configureSessionFactoryBuilder( sfb ); + sfb.applyStatisticsSupport( true ); + sfb.applySecondLevelCacheSupport( false ); + sfb.applyQueryCacheSupport( false ); + } + + @Override + protected void applyMetadataSources(MetadataSources sources) { + super.applyMetadataSources( sources ); + sources.addAnnotatedClass( User.class ); + sources.addAnnotatedClass( Office.class ); + sources.addAnnotatedClass( Client.class ); + sources.addAnnotatedClass( Request.class ); + sources.addAnnotatedClass( InternalRequest.class ); + sources.addAnnotatedClass( Phone.class ); + } + + private Long userId; + private Long targetUserId; + private Long officeId; + + @Before + public void setUp() { + inTransaction( + session -> { + Log log = new Log(); + log.setCreationDate( OffsetDateTime.now() ); + + + Office office = buildOffice( "The office", "And", Collections.emptySet() ); + + session.persist( office ); + officeId = office.getId(); + + User user = new User(); + user.setOffice( office ); + user.setClient( office.getClient() ); + user.setName( "Fab" ); + user.setLog( log ); + user.setEmail( "fab@hibernate.org" ); + + session.persist( user ); + + userId = user.getId(); + + user = new User(); + user.setOffice( office ); + user.setClient( office.getClient() ); + user.setName( "And" ); + user.setLog( log ); + user.setEmail( "and@hibernate.org" ); + + session.persist( user ); + targetUserId = user.getId(); + } + ); + } + + @After + public void tearDown() { + inTransaction( + session -> { + session.createQuery( "delete from Request" ).executeUpdate(); + session.createQuery( "delete from User" ).executeUpdate(); + session.createQuery( "delete from Office" ).executeUpdate(); + session.createQuery( "delete from Client" ).executeUpdate(); + session.createQuery( "delete from Phone" ).executeUpdate(); + } + ); + } + + @Test + public void testPersist() { + final Statistics stats = sessionFactory().getStatistics(); + stats.clear(); + + InternalRequest internalRequest = new InternalRequest( 1L ); + inTransaction( + session -> { + User user = session.find( User.class, userId ); + internalRequest.setUser( user ); + assertThat( stats.getPrepareStatementCount(), is( 1L ) ); + + User targetUser = session.find( User.class, targetUserId ); + assertThat( stats.getPrepareStatementCount(), is( 2L ) ); + + internalRequest.setTargetUser( targetUser ); + + session.persist( internalRequest ); + + } + ); + assertThat( stats.getPrepareStatementCount(), is( 3L ) ); + } + + @Test + public void testDelete() { + Set officePhones = new HashSet<>(); + officePhones.add( new Phone( 1L, "landline", "028-234-9876" ) ); + officePhones.add( new Phone( 2L, "mobile", "072-122-9876" ) ); + Office office = buildOffice( "second office", "Fab", officePhones ); + inTransaction( + session -> { + session.save( office ); + } + ); + + inTransaction( + session -> { + Office result = session.find( Office.class, office.id ); + session.delete( result ); + } + ); + + + inTransaction( + session -> { + List offices = session.createQuery( "from Office" ).list(); + assertThat( offices.size(), is( 1 ) ); + assertThat( offices.get( 0 ).getId(), is( officeId ) ); + + List phones = session.createQuery( "from Phone" ).list(); + assertThat( phones.size(), is( 0 ) ); + } + ); + } + + @Test + public void testUpdate() { + InternalRequest internalRequest = new InternalRequest( 1L ); + inTransaction( + session -> { + User user = session.find( User.class, userId ); + internalRequest.setUser( user ); + + User targetUser = session.find( User.class, targetUserId ); + + internalRequest.setTargetUser( targetUser ); + + session.persist( internalRequest ); + } + ); + + inTransaction( + session -> { + InternalRequest result = session.find( InternalRequest.class, internalRequest.getId() ); + assertThat( result.getTargetUser().getId(), is( targetUserId ) ); + assertThat( result.getUser().getId(), is( userId ) ); + result.setUser( null ); + } + ); + + inTransaction( + session -> { + InternalRequest result = session.find( InternalRequest.class, internalRequest.getId() ); + assertThat( result.getTargetUser().getId(), is( targetUserId ) ); + assertThat( result.getUser(), is( nullValue() ) ); + + User user = session.find( User.class, userId ); + result.setTargetUser( user ); + + } + ); + + inTransaction( + session -> { + InternalRequest result = session.find( InternalRequest.class, internalRequest.getId() ); + assertThat( result.getTargetUser().getId(), is( userId ) ); + assertThat( result.getUser(), is( nullValue() ) ); + + User user = session.find( User.class, userId ); + result.setUser( user ); + + Set officePhones = new HashSet<>(); + officePhones.add( new Phone( 1L, "landline", "028-234-9876" ) ); + officePhones.add( new Phone( 2L, "mobile", "072-122-9876" ) ); + Office office = buildOffice( "second office", "Fab", officePhones ); + + + session.save( office ); + + List offices = new ArrayList<>(); + offices.add( office ); + + user.setOffices( offices ); + } + ); + + inTransaction( + session -> { + InternalRequest result = session.find( InternalRequest.class, internalRequest.getId() ); + assertThat( result.getTargetUser().getId(), is( userId ) ); + User user = result.getUser(); + assertThat( user.getId(), is( userId ) ); + List offices = user.getOffices(); + assertThat( offices.size(), is( 1 ) ); + Office office = offices.get( 0 ); + assertThat( office.getPhones().size(), is( 2 ) ); + } + ); + } + + private Office buildOffice(String officename, String clientName, Set phones) { + Log log = new Log(); + log.setCreationDate( OffsetDateTime.now() ); + + Office office; + office = new Office(); + Client client = new Client(); + client.setName( clientName ); + client.setLog( log ); + + office.setName( officename ); + office.setActive( true ); + office.setDescription( officename ); + office.setManaged( true ); + office.setLog( log ); + office.setClient( client ); + office.setPhones( phones ); + + return office; + } + + @Embeddable + public static class Log { + @Column(name = "`creationDate`", nullable = false) + private OffsetDateTime creationDate; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "`idCreator`") + private User creator; + + public OffsetDateTime getCreationDate() { + return creationDate; + } + + public void setCreationDate(OffsetDateTime creationDate) { + this.creationDate = creationDate; + } + + + public User getCreator() { + return creator; + } + + public void setCreator(User creator) { + this.creator = creator; + } + } + + @Entity(name = "User") + @Table(name = "`User`") + public static class User { + @Id + @GeneratedValue + private Long id; + + @Column(length = 120, nullable = false) + private String name; + + @Column(length = 200, nullable = false, unique = true) + private String email; + + private String hash; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "idOffice") + private Office office; + + @OneToMany + private List offices; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "`idClient`") + private Client client; + + @Embedded + private Log log = new Log(); + + public Long getId() { + return id; + } + + public Office getOffice() { + return office; + } + + public void setOffice(Office office) { + this.office = office; + } + + public Log getLog() { + return log; + } + + public void setLog(Log log) { + this.log = log; + } + + public Client getClient() { + return client; + } + + public void setClient(Client client) { + this.client = client; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getHash() { + return hash; + } + + public void setHash(String hash) { + this.hash = hash; + } + + public void setId(Long id) { + this.id = id; + } + + public List getOffices() { + return offices; + } + + public void setOffices(List offices) { + this.offices = offices; + } + } + + @Entity(name = "Office") + public static class Office { + + @Id + @GeneratedValue + private Long id; + + @Column(length = 50, nullable = false) + private String name; + + private String description; + + @Column(nullable = false) + private Boolean isActive = true; + + @Column(nullable = false) + private Boolean isManaged = false; + + @ManyToOne(optional = false, cascade = CascadeType.ALL) + @JoinColumn(name = "idClient") + private Client client; + + @OneToMany(cascade = CascadeType.ALL) + private Set phones = new HashSet<>(); + + @Embedded + private Log log = new Log(); + + public Long getId() { + return id; + } + + public Client getClient() { + return client; + } + + public void setClient(Client client) { + this.client = client; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Boolean getActive() { + return isActive; + } + + public void setActive(Boolean active) { + isActive = active; + } + + public Boolean getManaged() { + return isManaged; + } + + public void setManaged(Boolean managed) { + isManaged = managed; + } + + public Log getLog() { + return log; + } + + public void setLog(Log log) { + this.log = log; + } + + public void setId(Long id) { + this.id = id; + } + + public Set getPhones() { + return phones; + } + + public void setPhones(Set phones) { + this.phones = phones; + } + } + + @Entity(name = "Phone") + public static class Phone { + @Id + private Long id; + + private String type; + + @NaturalId + @Column(name = "`number`") + private String number; + + public Phone() { + } + + public Phone(Long id, String type, String number) { + this.id = id; + this.type = type; + this.number = number; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getNumber() { + return number; + } + + public void setNumber(String number) { + this.number = number; + } + } + + @Entity(name = "Client") + public static class Client { + + @Id + @GeneratedValue + private Long id; + + private String name; + + @Embedded + private Log log = new Log(); + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public void setId(Long id) { + this.id = id; + } + + public Log getLog() { + return log; + } + + public void setLog(Log log) { + this.log = log; + } + } + + @Inheritance(strategy = InheritanceType.SINGLE_TABLE) + @DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING, name = "type", length = 30) + @Cacheable + @Entity(name = "Request") + public static abstract class Request { + + @Id + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "idUser") + private User user; + + @Column(name = "`creationDate`", nullable = false) + private OffsetDateTime creationDate = OffsetDateTime.now(); + + @Enumerated(EnumType.STRING) + @Column(length = 30, nullable = false, name = "status") + private StatusRequest status = StatusRequest.REQUESTED; + + Request() { + } + + public Request(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public User getUser() { + return user; + } + + public void setUser(User userSolicitacao) { + this.user = userSolicitacao; + } + + public OffsetDateTime getCreationDate() { + return creationDate; + } + + public void setCreationDate(OffsetDateTime creationDate) { + this.creationDate = creationDate; + } + + public StatusRequest getStatus() { + return status; + } + + public void setStatus(StatusRequest status) { + this.status = status; + } + + public void setId(Long id) { + this.id = id; + } + } + + @Entity(name = "InternalRequest") + @DiscriminatorValue(value = "INTERN") + public static class InternalRequest extends Request { + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "`idTargetUser`") + private User targetUser; + + InternalRequest() { + } + + public InternalRequest(Long id) { + super( id ); + } + + + public User getTargetUser() { + return targetUser; + } + + public void setTargetUser(User targetUser) { + this.targetUser = targetUser; + } + } + + public enum StatusRequest { + + REQUESTED( "requested" ), WAITING( "Feedback waiting" ); + + private final String description; + + StatusRequest(final String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/ManyToOneWithEmbeddedAndNotOptionalFieldTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/ManyToOneWithEmbeddedAndNotOptionalFieldTest.java new file mode 100644 index 0000000000..b502391e8f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/ManyToOneWithEmbeddedAndNotOptionalFieldTest.java @@ -0,0 +1,195 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking; + +import java.time.OffsetDateTime; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.SessionFactoryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.stat.Statistics; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author Andrea Boriero + */ +@RunWith(BytecodeEnhancerRunner.class) +@CustomEnhancementContext({ DirtyCheckEnhancementContext.class, NoDirtyCheckEnhancementContext.class }) +@TestForIssue(jiraKey = "HHH-13705") +public class ManyToOneWithEmbeddedAndNotOptionalFieldTest extends BaseNonConfigCoreFunctionalTestCase { + + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" ); + ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" ); + ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" ); + } + + @Override + protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) { + super.configureSessionFactoryBuilder( sfb ); + sfb.applyStatisticsSupport( true ); + sfb.applySecondLevelCacheSupport( false ); + sfb.applyQueryCacheSupport( false ); + } + + @Override + protected void applyMetadataSources(MetadataSources sources) { + super.applyMetadataSources( sources ); + sources.addAnnotatedClass( Client.class ); + sources.addAnnotatedClass( User.class ); + } + + private Long userId; + + @Before + public void setUp() { + User user = new User(); + inTransaction( + session -> { + + Log log = new Log(); + log.setCreationDate( OffsetDateTime.now() ); + + Client client = new Client(); + client.setName( "And" ); + client.setLog( log ); + + + session.save( client ); + + user.setName( "Fab" ); + + user.setClient( client ); + + session.save( user ); + } + ); + userId = user.getId(); + } + + + @Test + public void load() { + final Statistics stats = sessionFactory().getStatistics(); + stats.clear(); + inTransaction( + session -> { + session.find( User.class, userId ); + } + ); + assertThat( stats.getPrepareStatementCount(), is( 1L ) ); + } + + @Entity(name = "Client") + public static class Client { + + @Id + @GeneratedValue + private Long id; + + private String name; + + @Embedded + private Log log = new Log(); + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public void setId(Long id) { + this.id = id; + } + + public Log getLog() { + return log; + } + + public void setLog(Log log) { + this.log = log; + } + } + + @Entity(name = "User") + @Table(name = "`User`") + public static class User { + @Id + @GeneratedValue + private Long id; + + @Column(length = 120, nullable = false) + private String name; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "`idClient`") + private Client client; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Client getClient() { + return client; + } + + public void setClient(Client client) { + this.client = client; + } + } + + @Embeddable + public static class Log { + @Column(name = "`creationDate`", nullable = false) + private OffsetDateTime creationDate; + + public OffsetDateTime getCreationDate() { + return creationDate; + } + + public void setCreationDate(OffsetDateTime creationDate) { + this.creationDate = creationDate; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/NoDirtyCheckEnhancementContext.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/NoDirtyCheckEnhancementContext.java new file mode 100644 index 0000000000..b6a9883ee4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/NoDirtyCheckEnhancementContext.java @@ -0,0 +1,26 @@ +/* + * 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.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking; + +import org.hibernate.bytecode.enhance.spi.UnloadedClass; + +import org.hibernate.testing.bytecode.enhancement.EnhancerTestContext; + +/** + * @author Andrea Boriero + */ +public class NoDirtyCheckEnhancementContext extends EnhancerTestContext { + @Override + public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) { + return false; + } + + @Override + public boolean doExtendedEnhancement(UnloadedClass classDescriptor) { + return false; + } +} From f5fb84cfe29c15e79cd4903d6f380d75bf129442 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 5 Nov 2019 16:20:45 +0000 Subject: [PATCH 12/23] HHH-13705 Enhancement as Proxy with inline dirty checking - flush of an @ManyToOne with an Embedded value having not null properties causes PropertyValueException --- .../entity/BytecodeEnhancementMetadataPojoImpl.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/BytecodeEnhancementMetadataPojoImpl.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/BytecodeEnhancementMetadataPojoImpl.java index cad2b64c9f..1496c2cbed 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/BytecodeEnhancementMetadataPojoImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/BytecodeEnhancementMetadataPojoImpl.java @@ -20,6 +20,7 @@ import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistentAttributeInterceptable; import org.hibernate.engine.spi.PersistentAttributeInterceptor; +import org.hibernate.engine.spi.SelfDirtinessTracker; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.Status; import org.hibernate.mapping.PersistentClass; @@ -140,8 +141,14 @@ public final class BytecodeEnhancementMetadataPojoImpl implements BytecodeEnhanc final PersistenceContext persistenceContext = session.getPersistenceContext(); // first, instantiate the entity instance to use as the proxy - final PersistentAttributeInterceptable entity = (PersistentAttributeInterceptable) persister.getEntityTuplizer().instantiate( identifier, session ); + final EntityTuplizer entityTuplizer = persister.getEntityTuplizer(); + final PersistentAttributeInterceptable entity = (PersistentAttributeInterceptable) entityTuplizer + .instantiate( identifier, session ); + // clear the fields that are marked as dirty in the dirtyness tracker + if ( entity instanceof SelfDirtinessTracker ) { + ( (SelfDirtinessTracker) entity ).$$_hibernate_clearDirtyAttributes(); + } // add the entity (proxy) instance to the PC persistenceContext.addEnhancedProxy( entityKey, entity ); From 52f9a36a6dab5c9709d8b08f9653cc2e7d6df975 Mon Sep 17 00:00:00 2001 From: Vlad Mihalcea Date: Sat, 7 Sep 2019 04:03:59 +0300 Subject: [PATCH 13/23] HHH-13614 Allow the IntegratorProvider to be supplied via its FQN in the JPA persistence.xml --- .../EntityManagerFactoryBuilderImpl.java | 94 ++++++++---- .../test/query/ConstructorResultDtoTest.java | 139 ++++++++++++++++++ 2 files changed, 201 insertions(+), 32 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/jpa/test/query/ConstructorResultDtoTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java index 4f5885a473..f2a777db8c 100644 --- a/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java @@ -276,37 +276,11 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil return; } - MetadataBuilderContributor metadataBuilderContributor = null; - Class metadataBuilderContributorImplClass = null; - - if ( metadataBuilderContributorSetting instanceof MetadataBuilderContributor ) { - metadataBuilderContributor = (MetadataBuilderContributor) metadataBuilderContributorSetting; - } - else if ( metadataBuilderContributorSetting instanceof Class ) { - metadataBuilderContributorImplClass = (Class) metadataBuilderContributorSetting; - } - else if ( metadataBuilderContributorSetting instanceof String ) { - final ClassLoaderService classLoaderService = standardServiceRegistry.getService( ClassLoaderService.class ); - - metadataBuilderContributorImplClass = classLoaderService.classForName( (String) metadataBuilderContributorSetting ); - } - else { - throw new IllegalArgumentException( - "The provided " + METADATA_BUILDER_CONTRIBUTOR + " setting value [" + metadataBuilderContributorSetting + "] is not supported!" - ); - } - - if ( metadataBuilderContributorImplClass != null ) { - try { - metadataBuilderContributor = metadataBuilderContributorImplClass.newInstance(); - } - catch (InstantiationException | IllegalAccessException e) { - throw new IllegalArgumentException( - "The MetadataBuilderContributor class [" + metadataBuilderContributorImplClass + "] could not be instantiated!", - e - ); - } - } + MetadataBuilderContributor metadataBuilderContributor = loadSettingInstance( + METADATA_BUILDER_CONTRIBUTOR, + metadataBuilderContributorSetting, + MetadataBuilderContributor.class + ); if ( metadataBuilderContributor != null ) { metadataBuilderContributor.contribute( metamodelBuilder ); @@ -394,7 +368,12 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil ClassLoaderService providedClassLoaderService) { final BootstrapServiceRegistryBuilder bsrBuilder = new BootstrapServiceRegistryBuilder(); - final IntegratorProvider integratorProvider = (IntegratorProvider) integrationSettings.get( INTEGRATOR_PROVIDER ); + final IntegratorProvider integratorProvider = loadSettingInstance( + INTEGRATOR_PROVIDER, + integrationSettings.get( INTEGRATOR_PROVIDER ), + IntegratorProvider.class + ); + if ( integratorProvider != null ) { for ( Integrator integrator : integratorProvider.getIntegrators() ) { bsrBuilder.applyIntegrator( integrator ); @@ -1394,4 +1373,55 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil this.cacheRegionDefinitions.add( cacheRegionDefinition ); } } + + private T loadSettingInstance(String settingName, Object settingValue, Class clazz) { + if ( settingValue == null ) { + return null; + } + + T instance = null; + Class instanceClass = null; + + if ( clazz.isAssignableFrom( settingValue.getClass() ) ) { + instance = (T) settingValue; + } + else if ( settingValue instanceof Class ) { + instanceClass = (Class) settingValue; + } + else if ( settingValue instanceof String ) { + String settingStringValue = (String) settingValue; + if ( standardServiceRegistry != null ) { + final ClassLoaderService classLoaderService = standardServiceRegistry.getService( ClassLoaderService.class ); + + instanceClass = classLoaderService.classForName( settingStringValue ); + } + else { + try { + instanceClass = (Class) Class.forName( settingStringValue ); + } + catch (ClassNotFoundException e) { + throw new IllegalArgumentException( "Can't load class: " + settingStringValue, e ); + } + } + } + else { + throw new IllegalArgumentException( + "The provided " + settingName + " setting value [" + settingValue + "] is not supported!" + ); + } + + if ( instanceClass != null ) { + try { + instance = instanceClass.newInstance(); + } + catch (InstantiationException | IllegalAccessException e) { + throw new IllegalArgumentException( + "The MetadataBuilderContributor class [" + instanceClass + "] could not be instantiated!", + e + ); + } + } + + return instance; + } } diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/query/ConstructorResultDtoTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/query/ConstructorResultDtoTest.java new file mode 100644 index 0000000000..3d46188919 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/query/ConstructorResultDtoTest.java @@ -0,0 +1,139 @@ +/* + * 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.jpa.test.query; + +import java.util.*; +import javax.persistence.*; + +import org.hibernate.FlushMode; +import org.hibernate.Session; +import org.hibernate.boot.Metadata; +import org.hibernate.dialect.Oracle8iDialect; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.integrator.spi.Integrator; +import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl; +import org.hibernate.jpa.boot.spi.IntegratorProvider; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.service.spi.SessionFactoryServiceRegistry; +import org.hibernate.testing.SkipForDialect; +import org.junit.Test; + +import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author Vlad Mihalcea + */ +public class ConstructorResultDtoTest extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[]{ + Person.class + }; + } + + @Test + public void test() { + doInJPA(this::entityManagerFactory, entityManager -> { + List dtos = entityManager.createQuery( + "select new PersonDto(id, name) " + + "from Person", PersonDto.class) + .getResultList(); + }); + } + + @Override + protected void addMappings(Map settings) { + settings.put(EntityManagerFactoryBuilderImpl.INTEGRATOR_PROVIDER, DtoIntegratorProvider.class.getName()); + } + + public static class DtoIntegratorProvider implements IntegratorProvider { + @Override + public List getIntegrators() { + return Collections.singletonList( + new Integrator() { + @Override + public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { + metadata.getImports().put("PersonDto", PersonDto.class.getName()); + } + + @Override + public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { + + } + } + ); + } + } + + @Entity(name = "Person") + public static class Person { + + @Id + @GeneratedValue + private Long id; + + private String name; + + private int age; + + public Person() { + } + + public Person(String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + } + + public static class PersonDto { + + private Long id; + + private String name; + + public PersonDto(Long id, String name) { + this.id = id; + this.name = name; + } + + public void setId(Long id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + } +} From 841368175f68c7d44e4c74c09852809a66a83e8b Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 3 Oct 2019 16:01:31 +0100 Subject: [PATCH 14/23] HHH-13614 Allow the IntegratorProvider to be supplied via its FQN in the JPA persistence.xml --- .../EntityManagerFactoryBuilderImpl.java | 37 +++-- .../DtoIntegratorProvider.java | 43 ++++++ ...IntegrationProviderSettingByClassTest.java | 48 ++++++ ...ntegrationProviderSettingByObjectTest.java | 48 ++++++ ...ntegrationProviderSettingByStringTest.java | 48 ++++++ .../jpa/test/integrationprovider/Person.java | 49 ++++++ .../test/integrationprovider/PersonDto.java | 37 +++++ .../test/query/ConstructorResultDtoTest.java | 139 ------------------ 8 files changed, 294 insertions(+), 155 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/DtoIntegratorProvider.java create mode 100644 hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/IntegrationProviderSettingByClassTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/IntegrationProviderSettingByObjectTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/IntegrationProviderSettingByStringTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/Person.java create mode 100644 hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/PersonDto.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/jpa/test/query/ConstructorResultDtoTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java index f2a777db8c..a64bde63f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java @@ -368,18 +368,8 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil ClassLoaderService providedClassLoaderService) { final BootstrapServiceRegistryBuilder bsrBuilder = new BootstrapServiceRegistryBuilder(); - final IntegratorProvider integratorProvider = loadSettingInstance( - INTEGRATOR_PROVIDER, - integrationSettings.get( INTEGRATOR_PROVIDER ), - IntegratorProvider.class - ); + applyIntegrationProvider( integrationSettings, bsrBuilder ); - if ( integratorProvider != null ) { - for ( Integrator integrator : integratorProvider.getIntegrators() ) { - bsrBuilder.applyIntegrator( integrator ); - } - } - final StrategyRegistrationProviderList strategyRegistrationProviderList = (StrategyRegistrationProviderList) integrationSettings.get( STRATEGY_REGISTRATION_PROVIDERS ); if ( strategyRegistrationProviderList != null ) { @@ -443,6 +433,25 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil return bsrBuilder.build(); } + private void applyIntegrationProvider(Map integrationSettings, BootstrapServiceRegistryBuilder bsrBuilder) { + Object integrationSetting = integrationSettings.get( INTEGRATOR_PROVIDER ); + if ( integrationSetting == null ) { + return; + } + final IntegratorProvider integratorProvider = loadSettingInstance( + INTEGRATOR_PROVIDER, + integrationSetting, + IntegratorProvider.class + ); + + if ( integratorProvider != null ) { + for ( Integrator integrator : integratorProvider.getIntegrators() ) { + bsrBuilder.applyIntegrator( integrator ); + } + } + } + + @SuppressWarnings("unchecked") private MergedSettings mergeSettings( PersistenceUnitDescriptor persistenceUnit, Map integrationSettings, @@ -1375,10 +1384,6 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil } private T loadSettingInstance(String settingName, Object settingValue, Class clazz) { - if ( settingValue == null ) { - return null; - } - T instance = null; Class instanceClass = null; @@ -1416,7 +1421,7 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil } catch (InstantiationException | IllegalAccessException e) { throw new IllegalArgumentException( - "The MetadataBuilderContributor class [" + instanceClass + "] could not be instantiated!", + "The " + clazz.getSimpleName() +" class [" + instanceClass + "] could not be instantiated!", e ); } diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/DtoIntegratorProvider.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/DtoIntegratorProvider.java new file mode 100644 index 0000000000..b71ce808b7 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/DtoIntegratorProvider.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.jpa.test.integrationprovider; + +import java.util.Collections; +import java.util.List; + +import org.hibernate.boot.Metadata; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.integrator.spi.Integrator; +import org.hibernate.jpa.boot.spi.IntegratorProvider; +import org.hibernate.service.spi.SessionFactoryServiceRegistry; + +/** + * @author Andrea Boriero + */ +public class DtoIntegratorProvider implements IntegratorProvider { + @Override + public List getIntegrators() { + return Collections.singletonList( + new Integrator() { + @Override + public void integrate( + Metadata metadata, + SessionFactoryImplementor sessionFactory, + SessionFactoryServiceRegistry serviceRegistry) { + metadata.getImports().put( "PersonDto", PersonDto.class.getName() ); + } + + @Override + public void disintegrate( + SessionFactoryImplementor sessionFactory, + SessionFactoryServiceRegistry serviceRegistry) { + + } + } + ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/IntegrationProviderSettingByClassTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/IntegrationProviderSettingByClassTest.java new file mode 100644 index 0000000000..53595d2c9c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/IntegrationProviderSettingByClassTest.java @@ -0,0 +1,48 @@ +/* + * 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.jpa.test.integrationprovider; + +import java.util.List; +import java.util.Map; + +import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; + +/** + * @author Vlad Mihalcea + */ +@TestForIssue(jiraKey = "HHH-13614") +public class IntegrationProviderSettingByClassTest extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Person.class + }; + } + + @Test + public void test() { + doInJPA( this::entityManagerFactory, entityManager -> { + List dtos = entityManager.createQuery( + "select new PersonDto(id, name) " + + "from Person", PersonDto.class ) + .getResultList(); + } ); + } + + @Override + protected void addMappings(Map settings) { + settings.put( EntityManagerFactoryBuilderImpl.INTEGRATOR_PROVIDER, DtoIntegratorProvider.class ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/IntegrationProviderSettingByObjectTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/IntegrationProviderSettingByObjectTest.java new file mode 100644 index 0000000000..52274ea9c4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/IntegrationProviderSettingByObjectTest.java @@ -0,0 +1,48 @@ +/* + * 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.jpa.test.integrationprovider; + +import java.util.List; +import java.util.Map; + +import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; + +/** + * @author Vlad Mihalcea + */ +@TestForIssue(jiraKey = "HHH-13614") +public class IntegrationProviderSettingByObjectTest extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Person.class + }; + } + + @Test + public void test() { + doInJPA( this::entityManagerFactory, entityManager -> { + List dtos = entityManager.createQuery( + "select new PersonDto(id, name) " + + "from Person", PersonDto.class ) + .getResultList(); + } ); + } + + @Override + protected void addMappings(Map settings) { + settings.put( EntityManagerFactoryBuilderImpl.INTEGRATOR_PROVIDER, new DtoIntegratorProvider() ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/IntegrationProviderSettingByStringTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/IntegrationProviderSettingByStringTest.java new file mode 100644 index 0000000000..ec0406e171 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/IntegrationProviderSettingByStringTest.java @@ -0,0 +1,48 @@ +/* + * 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.jpa.test.integrationprovider; + +import java.util.List; +import java.util.Map; + +import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; + +/** + * @author Vlad Mihalcea + */ +@TestForIssue(jiraKey = "HHH-13614") +public class IntegrationProviderSettingByStringTest extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Person.class + }; + } + + @Test + public void test() { + doInJPA( this::entityManagerFactory, entityManager -> { + List dtos = entityManager.createQuery( + "select new PersonDto(id, name) " + + "from Person", PersonDto.class ) + .getResultList(); + } ); + } + + @Override + protected void addMappings(Map settings) { + settings.put( EntityManagerFactoryBuilderImpl.INTEGRATOR_PROVIDER, DtoIntegratorProvider.class.getName() ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/Person.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/Person.java new file mode 100644 index 0000000000..af6929569d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/Person.java @@ -0,0 +1,49 @@ +/* + * 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.jpa.test.integrationprovider; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +/** + * @author Andrea Boriero + */ +@Entity(name = "Person") +public class Person { + + @Id + @GeneratedValue + private Long id; + + private String name; + + private int age; + + public Person() { + } + + public Person(String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/PersonDto.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/PersonDto.java new file mode 100644 index 0000000000..e69cf22e11 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/integrationprovider/PersonDto.java @@ -0,0 +1,37 @@ +/* + * 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.jpa.test.integrationprovider; + +/** + * @author Andrea Boriero + */ +public class PersonDto { + private Long id; + + private String name; + + public PersonDto(Long id, String name) { + this.id = id; + this.name = name; + } + + public void setId(Long id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/query/ConstructorResultDtoTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/query/ConstructorResultDtoTest.java deleted file mode 100644 index 3d46188919..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/query/ConstructorResultDtoTest.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * 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.jpa.test.query; - -import java.util.*; -import javax.persistence.*; - -import org.hibernate.FlushMode; -import org.hibernate.Session; -import org.hibernate.boot.Metadata; -import org.hibernate.dialect.Oracle8iDialect; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.integrator.spi.Integrator; -import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl; -import org.hibernate.jpa.boot.spi.IntegratorProvider; -import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; - -import org.hibernate.service.spi.SessionFactoryServiceRegistry; -import org.hibernate.testing.SkipForDialect; -import org.junit.Test; - -import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; -import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -/** - * @author Vlad Mihalcea - */ -public class ConstructorResultDtoTest extends BaseEntityManagerFunctionalTestCase { - - @Override - protected Class[] getAnnotatedClasses() { - return new Class[]{ - Person.class - }; - } - - @Test - public void test() { - doInJPA(this::entityManagerFactory, entityManager -> { - List dtos = entityManager.createQuery( - "select new PersonDto(id, name) " + - "from Person", PersonDto.class) - .getResultList(); - }); - } - - @Override - protected void addMappings(Map settings) { - settings.put(EntityManagerFactoryBuilderImpl.INTEGRATOR_PROVIDER, DtoIntegratorProvider.class.getName()); - } - - public static class DtoIntegratorProvider implements IntegratorProvider { - @Override - public List getIntegrators() { - return Collections.singletonList( - new Integrator() { - @Override - public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { - metadata.getImports().put("PersonDto", PersonDto.class.getName()); - } - - @Override - public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { - - } - } - ); - } - } - - @Entity(name = "Person") - public static class Person { - - @Id - @GeneratedValue - private Long id; - - private String name; - - private int age; - - public Person() { - } - - public Person(String name) { - this.name = name; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - } - - public static class PersonDto { - - private Long id; - - private String name; - - public PersonDto(Long id, String name) { - this.id = id; - this.name = name; - } - - public void setId(Long id) { - this.id = id; - } - - public void setName(String name) { - this.name = name; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - } -} From 0e329bef5a0f298362f3cd40b664c9f805b7947a Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Fri, 23 Aug 2019 12:51:47 -0400 Subject: [PATCH 15/23] HHH-12030 - Fix ClassCastException when Embeddable collection uses generic types --- .../JPAMetaModelEntityProcessor.java | 8 +++- .../embeddable/generics/ChildEmbeddable.java | 16 +++++++ .../generics/EmbeddableGenericsTest.java | 42 +++++++++++++++++++ .../test/embeddable/generics/MyTypeImpl.java | 13 ++++++ .../embeddable/generics/MyTypeInterface.java | 13 ++++++ .../embeddable/generics/ParentEmbeddable.java | 27 ++++++++++++ 6 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/ChildEmbeddable.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/EmbeddableGenericsTest.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/MyTypeImpl.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/MyTypeInterface.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/ParentEmbeddable.java diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.java index 19fd667611..5b1721fedb 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.java @@ -294,7 +294,13 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor { TypeMirror collectionElementType = TypeUtils.getCollectionElementType( declaredType, fqNameOfReturnType, null, context ); - returnedElement = (TypeElement) context.getTypeUtils().asElement( collectionElementType ); + + final Element collectionElement = context.getTypeUtils().asElement( collectionElementType ); + if ( ElementKind.TYPE_PARAMETER.equals( collectionElement.getKind() ) ) { + return Boolean.FALSE; + } + + returnedElement = (TypeElement) collectionElement; } if ( type.getQualifiedName().toString().equals( returnedElement.getQualifiedName().toString() ) ) { diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/ChildEmbeddable.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/ChildEmbeddable.java new file mode 100644 index 0000000000..d3a3fcfef9 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/ChildEmbeddable.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 . + */ +package org.hibernate.jpamodelgen.test.embeddable.generics; + +import javax.persistence.Embeddable; + +/** + * @author Chris Cranford + */ +@Embeddable +public class ChildEmbeddable extends ParentEmbeddable { +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/EmbeddableGenericsTest.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/EmbeddableGenericsTest.java new file mode 100644 index 0000000000..5252087f9c --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/EmbeddableGenericsTest.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 . + */ +package org.hibernate.jpamodelgen.test.embeddable.generics; + +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; + +import static org.hibernate.jpamodelgen.test.util.TestUtil.assertMetamodelClassGeneratedFor; +import static org.hibernate.jpamodelgen.test.util.TestUtil.assertSetAttributeTypeInMetaModelFor; +import static org.hibernate.jpamodelgen.test.util.TestUtil.assertSuperClassRelationShipInMetamodel; + +/** + * @author Chris Cranford + */ +@TestForIssue(jiraKey = "HHH_12030") +public class EmbeddableGenericsTest extends CompilationTest { + @Test + @WithClasses({ ChildEmbeddable.class, ParentEmbeddable.class }) + public void testGeneratingEmbeddablesWithGenerics() { + assertMetamodelClassGeneratedFor( ChildEmbeddable.class ); + assertMetamodelClassGeneratedFor( ParentEmbeddable.class ); + + assertSetAttributeTypeInMetaModelFor( + ParentEmbeddable.class, + "fields", + MyTypeInterface.class, + "Expected Set for attribute named 'fields'" + ); + + assertSuperClassRelationShipInMetamodel( + ChildEmbeddable.class, + ParentEmbeddable.class + ); + } +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/MyTypeImpl.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/MyTypeImpl.java new file mode 100644 index 0000000000..87e0c373e1 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/MyTypeImpl.java @@ -0,0 +1,13 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.jpamodelgen.test.embeddable.generics; + +/** + * @author Chris Cranford + */ +public class MyTypeImpl implements MyTypeInterface { +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/MyTypeInterface.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/MyTypeInterface.java new file mode 100644 index 0000000000..b2ea67cf56 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/MyTypeInterface.java @@ -0,0 +1,13 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.jpamodelgen.test.embeddable.generics; + +/** + * @author Chris Cranford + */ +public interface MyTypeInterface { +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/ParentEmbeddable.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/ParentEmbeddable.java new file mode 100644 index 0000000000..edd1d9ac5e --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/embeddable/generics/ParentEmbeddable.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 . + */ +package org.hibernate.jpamodelgen.test.embeddable.generics; + +import java.util.Set; + +import javax.persistence.Embeddable; + +/** + * @author Chris Cranford + */ +@Embeddable +public class ParentEmbeddable { + private Set fields; + + public Set getFields() { + return fields; + } + + public void setFields(Set fields) { + this.fields = fields; + } +} From 5fb866403e41fdfcd60fcb19d06a76e2be0fe744 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 14 Nov 2019 15:42:14 +0000 Subject: [PATCH 16/23] 5.4.9 --- changelog.txt | 30 ++++++++++++++++++++++++++++++ gradle/base-information.gradle | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 8bc97938f0..7245e0a971 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,36 @@ Hibernate 5 Changelog Note: Please refer to JIRA to learn more about each issue. +Changes in 5.4.9.Final (November 14, 2019) +------------------------------------------------------------------------------------------------------------------------ + +https://hibernate.atlassian.net/projects/HHH/versions/31806/tab/release-report-done + +** Bug + * [HHH-12030] - Symbol$TypeVariableSymbol cannot be cast to TypeElement + * [HHH-13307] - On release of batch it still contained JDBC statements using JTA + * [HHH-13433] - EntityManager.find() should only check for roll-back-only condition if there is an active JTA transaction, otherwise ORM should throw convert( e, lockOptions ) + * [HHH-13614] - Allow the IntegratorProvider to be supplied via its FQN in the JPA persistence.xml + * [HHH-13670] - Missing from clause in query with joined inheritance, regression in 5.4.5 + * [HHH-13687] - TenantSchemaResolver not called in integration test after upgrade from 5.4.4 to >=5.4.5 + * [HHH-13690] - Multi-tenancy supporting session factories can not be created + * [HHH-13698] - Hibernate does not recognize MySQL 8 error code 3572 as PessimisticLockException + * [HHH-13700] - Configuration property CONNECTION_PROVIDER_DISABLES_AUTOCOMMIT should not be passed to the JDBC connection properties + * [HHH-13705] - Enhancement as Proxy with inline dirty checking - flush of an @ManyToOne with an Embedded value having not null properties causes PropertyValueException + * [HHH-13710] - Wrong tenant-identifier in Envers temporary session + * [HHH-13712] - inheritance - select count query is not working with inheritance + * [HHH-13727] - h2 database with DATABASE_TO_UPPER=false throws org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "sequences" not found + +** Task + * [HHH-13730] - Upgrade to Classmate 1.4.0 + * [HHH-13731] - Upgrade to Classmate 1.5.1 + * [HHH-13733] - Upgrade to Jandex 2.1.1.Final + +** Improvement + * [HHH-13654] - Avoid clearing of collections when closing StatefulPersistenceContext + * [HHH-13723] - Hint sizing of ArrayList in ResultSetProcessingContextImpl + + Changes in 5.4.8.Final (October 28, 2019) ------------------------------------------------------------------------------------------------------------------------ diff --git a/gradle/base-information.gradle b/gradle/base-information.gradle index 7b4b6b7a92..a086d64310 100644 --- a/gradle/base-information.gradle +++ b/gradle/base-information.gradle @@ -8,7 +8,7 @@ apply plugin: 'base' ext { - ormVersion = new HibernateVersion( '5.4.9-SNAPSHOT', project ) + ormVersion = new HibernateVersion( '5.4.9.Final', project ) baselineJavaVersion = '1.8' jpaVersion = new JpaVersion('2.2') } From 7a74e4dd8f2a6eccb08fe1d6cae3fa47426e8f11 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 14 Nov 2019 17:18:14 +0000 Subject: [PATCH 17/23] 5.4.9 --- gradle/base-information.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/base-information.gradle b/gradle/base-information.gradle index a086d64310..f090fe062d 100644 --- a/gradle/base-information.gradle +++ b/gradle/base-information.gradle @@ -8,7 +8,7 @@ apply plugin: 'base' ext { - ormVersion = new HibernateVersion( '5.4.9.Final', project ) + ormVersion = new HibernateVersion( '5.4.10-SNAPSHOT', project ) baselineJavaVersion = '1.8' jpaVersion = new JpaVersion('2.2') } From c02011ef40c5df337e068cd75f8c448a31e09ec5 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 14 Nov 2019 13:28:56 -0800 Subject: [PATCH 18/23] HHH-13737 : Add test case for HHH-13433 --- ...nSessionFindJdbcExceptionHandlingTest.java | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/exceptionhandling/NonActiveTransactionSessionFindJdbcExceptionHandlingTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/exceptionhandling/NonActiveTransactionSessionFindJdbcExceptionHandlingTest.java b/hibernate-core/src/test/java/org/hibernate/test/exceptionhandling/NonActiveTransactionSessionFindJdbcExceptionHandlingTest.java new file mode 100644 index 0000000000..34072386cb --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/exceptionhandling/NonActiveTransactionSessionFindJdbcExceptionHandlingTest.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 . + */ +package org.hibernate.test.exceptionhandling; + +import java.sql.SQLException; +import java.util.Map; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EntityManager; +import javax.persistence.Id; +import javax.persistence.PersistenceException; + +import org.hibernate.JDBCException; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.testing.RequiresDialect; +import org.hibernate.testing.TestForIssue; +import org.junit.Before; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +@TestForIssue( jiraKey = "HHH-13737") +@RequiresDialect(H2Dialect.class) +public class NonActiveTransactionSessionFindJdbcExceptionHandlingTest extends BaseEntityManagerFunctionalTestCase { + + @Test + public void testJdbcExceptionThrown() { + // delete "description" column so that a JDBCException caused by a SQLException is thrown when looking up the AnEntity + doInJPA( + this::entityManagerFactory, + entityManager -> { + entityManager.createNativeQuery( "alter table AnEntity drop column description" ).executeUpdate(); + } + ); + + EntityManager entityManager = getOrCreateEntityManager(); + try { + entityManager.find( AnEntity.class, 1 ); + fail( "A PersistenceException should have been thrown." ); + } + catch ( PersistenceException ex ) { + assertTrue( JDBCException.class.isInstance( ex.getCause() ) ); + assertTrue( SQLException.class.isInstance( ex.getCause().getCause() ) ); + } + finally { + entityManager.close(); + } + } + + @Before + public void setupData() { + doInJPA( + this::entityManagerFactory, + entityManager -> { + entityManager.persist( new AnEntity( 1, "description" ) ); + } + ); + } + + @Override + @SuppressWarnings("unchecked") + protected void addMappings(Map settings) { + settings.put( AvailableSettings.JPA_TRANSACTION_COMPLIANCE, true); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { AnEntity.class }; + } + + @Entity(name = "AnEntity") + public static class AnEntity { + @Id + private int id; + @Column(name = "description") + private String description; + + AnEntity() { + } + + AnEntity(int id, String description) { + this.id = id; + this.description = description; + } + } +} From 7d8549d8aa08bbc4f28e5965bdf30d6b0a4b3062 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 14 Nov 2019 13:47:08 -0800 Subject: [PATCH 19/23] HHH-13737 : Add debug logging --- .../src/main/java/org/hibernate/internal/SessionImpl.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 48042a90ab..06791def7e 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -3348,7 +3348,13 @@ public final class SessionImpl } catch ( JDBCException e ) { if ( accessTransaction().isActive() && accessTransaction().getRollbackOnly() ) { - // assume this is the similar to the WildFly / IronJacamar "feature" described under HHH-12472 + // Assume this is the similar to the WildFly / IronJacamar "feature" described under HHH-12472. + // Just log the exception and return null. + if ( log.isDebugEnabled() ) { + log.debug( "JDBCException was thrown for a transaction marked for rollback; " + + "this is probably due to IronJacamar failing fast after " + + "transaction was marked for rollback.", e ); + } return null; } else { From 76308e8a3e62410be593c48340373754ca2b685e Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 14 Nov 2019 15:02:08 -0800 Subject: [PATCH 20/23] HHH-13737 : Correct log message and fix checkstyle failure --- .../src/main/java/org/hibernate/internal/SessionImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 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 06791def7e..ebd4613804 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -3352,8 +3352,8 @@ public final class SessionImpl // Just log the exception and return null. if ( log.isDebugEnabled() ) { log.debug( "JDBCException was thrown for a transaction marked for rollback; " + - "this is probably due to IronJacamar failing fast after " + - "transaction was marked for rollback.", e ); + "this is probably due to an operation failing fast due to the " + + "transaction marked for rollback.", e ); } return null; } From 7331a58d5ea3b3f62cb0874906943916030613a0 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Mon, 18 Nov 2019 14:51:36 +0000 Subject: [PATCH 21/23] HHH-13722 ArrayStoreException in Constraint.generateName --- .../main/java/org/hibernate/mapping/Constraint.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Constraint.java b/hibernate-core/src/main/java/org/hibernate/mapping/Constraint.java index cbaefbfb26..0624c06c59 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Constraint.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Constraint.java @@ -75,7 +75,16 @@ public abstract class Constraint implements RelationalModel, Exportable, Seriali * @return String The generated name */ public static String generateName(String prefix, Table table, List columns) { - return generateName( prefix, table, columns.toArray( new Column[columns.size()] ) ); + //N.B. legacy APIs are involved: can't trust that the columns List is actually + //containing Column instances - the generic type isn't consistently enforced. + ArrayList defensive = new ArrayList<>( columns.size() ); + for ( Object o : columns ) { + if ( o instanceof Column ) { + defensive.add( (Column) o ); + } + //else: others might be Formula instances. They don't need to be part of the name generation. + } + return generateName( prefix, table, defensive.toArray( new Column[0] ) ); } /** From be23e167af2c45d3dda8db679d86b2a3e872f8ea Mon Sep 17 00:00:00 2001 From: Andreas Knees Date: Fri, 15 Nov 2019 23:40:39 +0100 Subject: [PATCH 22/23] HHH-13722 Introducing a test case --- .../test/mapping/joinformula/ChildEntity.java | 25 +++++++++++ .../mapping/joinformula/JoinFormulaTest.java | 41 +++++++++++++++++++ .../mapping/joinformula/ParentEntity.java | 38 +++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/mapping/joinformula/ChildEntity.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/mapping/joinformula/JoinFormulaTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/mapping/joinformula/ParentEntity.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/mapping/joinformula/ChildEntity.java b/hibernate-core/src/test/java/org/hibernate/test/mapping/joinformula/ChildEntity.java new file mode 100644 index 0000000000..d30ae85398 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/mapping/joinformula/ChildEntity.java @@ -0,0 +1,25 @@ +/* + * 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.test.mapping.joinformula; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class ChildEntity { + + @Id + private Long id; + + @Column(name = "PARENT_ID") + private Long parentId; + + @Column + private String name; + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/mapping/joinformula/JoinFormulaTest.java b/hibernate-core/src/test/java/org/hibernate/test/mapping/joinformula/JoinFormulaTest.java new file mode 100644 index 0000000000..343b0bc65c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/mapping/joinformula/JoinFormulaTest.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 . + */ +package org.hibernate.test.mapping.joinformula; + +import org.hibernate.Session; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; + +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +public class JoinFormulaTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + ParentEntity.class, + ChildEntity.class + }; + } + + @Override + protected void configure(Configuration configuration) { + super.configure( configuration ); + configuration.setProperty( AvailableSettings.SHOW_SQL, Boolean.TRUE.toString() ); + configuration.setProperty( AvailableSettings.FORMAT_SQL, Boolean.TRUE.toString() ); + } + + @Test + public void hhh13722Test() { + try (Session s = openSession()) { + //Nothing to do: the test just needs to verify that + //this can boot. + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/mapping/joinformula/ParentEntity.java b/hibernate-core/src/test/java/org/hibernate/test/mapping/joinformula/ParentEntity.java new file mode 100644 index 0000000000..c34776e31a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/mapping/joinformula/ParentEntity.java @@ -0,0 +1,38 @@ +/* + * 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.test.mapping.joinformula; + +import org.hibernate.annotations.JoinColumnOrFormula; +import org.hibernate.annotations.JoinColumnsOrFormulas; +import org.hibernate.annotations.JoinFormula; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; + +@Entity +public class ParentEntity { + + @Id + private Long id; + + @OneToOne(targetEntity = ChildEntity.class, optional = false) + @JoinColumnsOrFormulas({ + @JoinColumnOrFormula(column = @JoinColumn(name = "ID", referencedColumnName = "PARENT_ID", insertable = false, updatable = false)), + @JoinColumnOrFormula(formula = @JoinFormula(referencedColumnName = "NAME", value = "'Tom'")) + }) + private ChildEntity tom; + + @OneToOne(targetEntity = ChildEntity.class, optional = false) + @JoinColumnsOrFormulas({ + @JoinColumnOrFormula(column = @JoinColumn(name = "ID", referencedColumnName = "PARENT_ID", insertable = false, updatable = false)), + @JoinColumnOrFormula(formula = @JoinFormula(referencedColumnName = "NAME", value = "'Ben'")) + }) + private ChildEntity ben; + +} From c9a373c1803ef63d45fbb871658fbf513165dcd0 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Mon, 18 Nov 2019 15:21:23 +0000 Subject: [PATCH 23/23] HHH-13739 Upgrade to Agroal 1.7 --- gradle/libraries.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libraries.gradle b/gradle/libraries.gradle index 799866b9c8..9168ef2b8c 100644 --- a/gradle/libraries.gradle +++ b/gradle/libraries.gradle @@ -25,7 +25,7 @@ ext { javassistVersion = '3.24.0-GA' byteBuddyVersion = '1.10.2' - agroalVersion = '1.6' + agroalVersion = '1.7' geolatteVersion = '1.4.0'