From 2226fda3315fee2a25426539687cdc6821fe1672 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Wed, 30 Jun 2021 18:57:20 +0200 Subject: [PATCH 1/3] Update SQL Server docker image URL as the old one does not work anymore --- docker_db.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker_db.sh b/docker_db.sh index 4f0cd16494..e88589cada 100755 --- a/docker_db.sh +++ b/docker_db.sh @@ -99,7 +99,7 @@ EOF mssql() { docker rm -f mssql || true - docker run --name mssql -d -p 1433:1433 -e "SA_PASSWORD=Hibernate_orm_test" -e ACCEPT_EULA=Y microsoft/mssql-server-linux:2017-CU13 + docker run --name mssql -d -p 1433:1433 -e "SA_PASSWORD=Hibernate_orm_test" -e ACCEPT_EULA=Y mcr.microsoft.com/mssql/server:2017-CU13 sleep 5 n=0 until [ "$n" -ge 5 ] From f168b8cff2c7fb41fc2d570ae2867f9e4b21fda0 Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Tue, 22 Jun 2021 16:33:43 +0800 Subject: [PATCH 2/3] HHH-14688 Get IdentifierGenerator from BeanContainer if not registered --- .../DefaultIdentifierGeneratorFactory.java | 31 ++++- .../UserDefinedGeneratorsTests.java | 131 ++++++++++++++++++ 2 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/idgen/userdefined/UserDefinedGeneratorsTests.java diff --git a/hibernate-core/src/main/java/org/hibernate/id/factory/internal/DefaultIdentifierGeneratorFactory.java b/hibernate-core/src/main/java/org/hibernate/id/factory/internal/DefaultIdentifierGeneratorFactory.java index 71c153ae4b..c2b80042ab 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/factory/internal/DefaultIdentifierGeneratorFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/id/factory/internal/DefaultIdentifierGeneratorFactory.java @@ -36,6 +36,9 @@ import org.hibernate.id.enhanced.TableGenerator; import org.hibernate.id.factory.spi.MutableIdentifierGeneratorFactory; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.resource.beans.container.spi.BeanContainer; +import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; +import org.hibernate.resource.beans.spi.ManagedBeanRegistry; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.spi.ServiceRegistryAwareService; import org.hibernate.service.spi.ServiceRegistryImplementor; @@ -46,6 +49,7 @@ import org.hibernate.type.Type; * * @author Steve Ebersole */ +@SuppressWarnings( { "deprecation" ,"rawtypes" ,"serial" } ) public class DefaultIdentifierGeneratorFactory implements MutableIdentifierGeneratorFactory, Serializable, ServiceRegistryAwareService { @@ -59,7 +63,6 @@ public class DefaultIdentifierGeneratorFactory /** * Constructs a new DefaultIdentifierGeneratorFactory. */ - @SuppressWarnings("deprecation") public DefaultIdentifierGeneratorFactory() { register( "uuid2", UUIDGenerator.class ); register( "guid", GUIDGenerator.class ); // can be done with UUIDGenerator + strategy @@ -109,11 +112,35 @@ public class DefaultIdentifierGeneratorFactory // } } + @SuppressWarnings("unchecked") @Override public IdentifierGenerator createIdentifierGenerator(String strategy, Type type, Properties config) { try { Class clazz = getIdentifierGeneratorClass( strategy ); - IdentifierGenerator identifierGenerator = ( IdentifierGenerator ) clazz.newInstance(); + BeanContainer beanContainer = serviceRegistry.getService(ManagedBeanRegistry.class).getBeanContainer(); + IdentifierGenerator identifierGenerator; + if ( generatorStrategyToClassNameMap.containsKey(strategy) || beanContainer == null ) { + identifierGenerator = ( IdentifierGenerator ) clazz.newInstance(); + } + else { + identifierGenerator = ( IdentifierGenerator ) beanContainer.getBean( + clazz, + new BeanContainer.LifecycleOptions() { + + @Override + public boolean canUseCachedReferences() { + return false; + } + + @Override + public boolean useJpaCompliantCreation() { + return true; + } + + }, + FallbackBeanInstanceProducer.INSTANCE + ).getBeanInstance(); + } if ( identifierGenerator instanceof Configurable ) { ( ( Configurable ) identifierGenerator ).configure( type, config, serviceRegistry ); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/idgen/userdefined/UserDefinedGeneratorsTests.java b/hibernate-core/src/test/java/org/hibernate/test/idgen/userdefined/UserDefinedGeneratorsTests.java new file mode 100644 index 0000000000..e84c5681bf --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/idgen/userdefined/UserDefinedGeneratorsTests.java @@ -0,0 +1,131 @@ +/* + * 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.idgen.userdefined; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.times; + +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.boot.Metadata; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.id.IdentifierGenerator; +import org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.resource.beans.container.spi.BeanContainer; +import org.hibernate.resource.beans.container.spi.BeanContainer.LifecycleOptions; +import org.hibernate.resource.beans.container.spi.ContainedBean; +import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; +import org.hibernate.service.spi.ServiceRegistryImplementor; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.junit.Test; +import org.mockito.Mockito; + +/** + * @author Yanming Zhou + */ +@TestForIssue(jiraKey = "HHH-14688") +public class UserDefinedGeneratorsTests extends BaseUnitTestCase { + + @Test + public void testCreateGeneratorsByBeanContainer() { + + final BeanContainer beanContainer = Mockito.mock( BeanContainer.class ); + given(beanContainer.getBean( any(), any(), any() ) ).willAnswer( invocation -> { + LifecycleOptions options = (LifecycleOptions) invocation.getArguments()[1]; + assertThat( options.canUseCachedReferences(), is( false ) ); + assertThat( options.useJpaCompliantCreation(), is( true ) ); + return (ContainedBean) TestIdentifierGenerator::new; + } ); + + final StandardServiceRegistryBuilder ssrb = new StandardServiceRegistryBuilder(); + ssrb.applySetting( AvailableSettings.BEAN_CONTAINER, beanContainer ); + + final StandardServiceRegistry ssr = ssrb.build(); + final Metadata metadata = new MetadataSources( ssr ) + .addAnnotatedClass( Entity1.class ) + .addAnnotatedClass( Entity2.class ) + .buildMetadata(); + + final DefaultIdentifierGeneratorFactory generatorFactory = new DefaultIdentifierGeneratorFactory(); + generatorFactory.injectServices( (ServiceRegistryImplementor) ssr ); + + final PersistentClass entityBinding1 = metadata.getEntityBinding( Entity1.class.getName() ); + final PersistentClass entityBinding2 = metadata.getEntityBinding( Entity2.class.getName() ); + final IdentifierGenerator generator1 = entityBinding1.getRootClass().getIdentifier().createIdentifierGenerator( + generatorFactory, + new H2Dialect(), + "", + "", + entityBinding1.getRootClass() + ); + final IdentifierGenerator generator2 = entityBinding2.getRootClass().getIdentifier().createIdentifierGenerator( + generatorFactory, + new H2Dialect(), + "", + "", + entityBinding2.getRootClass() + ); + + then( beanContainer ).should( times( 2 ) ).getBean( same( TestIdentifierGenerator.class ), any( LifecycleOptions.class ), + same( FallbackBeanInstanceProducer.INSTANCE ) ); + + assertThat( generator1, is( instanceOf( TestIdentifierGenerator.class ) ) ); + assertThat( generator2, is( instanceOf( TestIdentifierGenerator.class ) ) ); + assertThat( generator1 == generator2, is( false ) ); // should not be same instance + + } + + @Entity( name = "Entity1" ) + @Table( name = "tbl_1" ) + public static class Entity1 { + @Id + @GeneratedValue( generator = "test" ) + @GenericGenerator( name = "test", strategy = "org.hibernate.test.idgen.userdefined.UserDefinedGeneratorsTests$TestIdentifierGenerator" ) + private Integer id; + } + + @Entity( name = "Entity2" ) + @Table( name = "tbl_2" ) + public static class Entity2 { + @Id + @GeneratedValue( generator = "test" ) + @GenericGenerator( name = "test", strategy = "org.hibernate.test.idgen.userdefined.UserDefinedGeneratorsTests$TestIdentifierGenerator" ) + private Integer id; + } + + public static class TestIdentifierGenerator implements IdentifierGenerator { + + private AtomicInteger count = new AtomicInteger(); + + @Override + public Serializable generate( SharedSessionContractImplementor session, Object obj ) { + return count.incrementAndGet(); + } + + } + +} From 834f125c07c6c1ed487c1f42b62a2c98b037addc Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Thu, 1 Jul 2021 16:10:05 +0100 Subject: [PATCH 3/3] HHH-14706 Improve error message on incompatible types due to mismatched classloader --- .../AbstractSharedSessionContract.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index 6b7a927765..e147da2c29 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -871,12 +871,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont // if we have only a single return expression, its java type should match the requested type final Type queryResultType = queryPlan.getTranslators()[0].getReturnTypes()[0]; if ( !resultClass.isAssignableFrom( queryResultType.getReturnedClass() ) ) { - throw new IllegalArgumentException( - "Type specified for TypedQuery [" + - resultClass.getName() + - "] is incompatible with query return type [" + - queryResultType.getReturnedClass() + "]" - ); + throw buildIncompatibleException( resultClass, queryResultType.getReturnedClass() ); } } else { @@ -1001,10 +996,23 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont } private IllegalArgumentException buildIncompatibleException(Class resultClass, Class actualResultClass) { - return new IllegalArgumentException( - "Type specified for TypedQuery [" + resultClass.getName() + - "] is incompatible with query return type [" + actualResultClass + "]" - ); + final String resultClassName = resultClass.getName(); + final String actualResultClassName = actualResultClass.getName(); + if ( resultClassName.equals( actualResultClassName ) ) { + return new IllegalArgumentException( + "Type specified for TypedQuery [" + resultClassName + + "] is incompatible with the query return type of the same name." + + " Both classes have the same name but are different as they have been loaded respectively by Classloaders " + + resultClass.getClassLoader().toString() + ", " + actualResultClass.getClassLoader().toString() + + ". This suggests a classloader bug in the Runtime executing Hibernate ORM, or in the integration code." + ); + } + else { + return new IllegalArgumentException( + "Type specified for TypedQuery [" + resultClassName + + "] is incompatible with query return type [" + actualResultClass + "]" + ); + } } @Override