diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AttributeConverterDefinition.java b/hibernate-core/src/main/java/org/hibernate/cfg/AttributeConverterDefinition.java index 2453874042..c4376ee3d3 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AttributeConverterDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AttributeConverterDefinition.java @@ -6,6 +6,7 @@ */ package org.hibernate.cfg; +import java.lang.reflect.Constructor; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -56,7 +57,9 @@ public class AttributeConverterDefinition implements AttributeConverterInfo { private static AttributeConverter instantiateAttributeConverter(Class attributeConverterClass) { try { - return attributeConverterClass.newInstance(); + Constructor constructor = attributeConverterClass.getDeclaredConstructor(); + constructor.setAccessible( true ); + return constructor.newInstance(); } catch (Exception e) { throw new AnnotationException( diff --git a/hibernate-core/src/main/java/org/hibernate/resource/beans/internal/FallbackBeanInstanceProducer.java b/hibernate-core/src/main/java/org/hibernate/resource/beans/internal/FallbackBeanInstanceProducer.java index 076bad4c27..838acb4842 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/beans/internal/FallbackBeanInstanceProducer.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/beans/internal/FallbackBeanInstanceProducer.java @@ -6,6 +6,8 @@ */ package org.hibernate.resource.beans.internal; +import java.lang.reflect.Constructor; + import org.hibernate.InstantiationException; import org.hibernate.resource.beans.spi.BeanInstanceProducer; @@ -35,7 +37,9 @@ public class FallbackBeanInstanceProducer implements BeanInstanceProducer { public B produceBeanInstance(Class beanType) { log.tracef( "Creating ManagedBean(%s) using direct instantiation", beanType.getName() ); try { - return beanType.newInstance(); + Constructor constructor = beanType.getDeclaredConstructor(); + constructor.setAccessible( true ); + return constructor.newInstance(); } catch (Exception e) { throw new InstantiationException( "Could not instantiate managed bean directly", beanType, e ); diff --git a/hibernate-core/src/test/java/org/hibernate/boot/model/process/internal/IntegerToVarcharConverter.java b/hibernate-core/src/test/java/org/hibernate/boot/model/process/internal/IntegerToVarcharConverter.java new file mode 100644 index 0000000000..d0d2247a7f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/boot/model/process/internal/IntegerToVarcharConverter.java @@ -0,0 +1,20 @@ +package org.hibernate.boot.model.process.internal; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +/** + * @author Vlad Mihalcea + */ +@Converter( autoApply = true ) +class IntegerToVarcharConverter implements AttributeConverter { + @Override + public String convertToDatabaseColumn(Integer attribute) { + return attribute == null ? null : attribute.toString(); + } + + @Override + public Integer convertToEntityAttribute(String dbData) { + return dbData == null ? null : Integer.valueOf( dbData ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/boot/model/process/internal/ScanningCoordinatorTest.java b/hibernate-core/src/test/java/org/hibernate/boot/model/process/internal/ScanningCoordinatorTest.java index 0f5789c9f3..4c55e3742b 100644 --- a/hibernate-core/src/test/java/org/hibernate/boot/model/process/internal/ScanningCoordinatorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/boot/model/process/internal/ScanningCoordinatorTest.java @@ -5,6 +5,7 @@ import java.net.URL; import java.util.Arrays; import java.util.Collections; +import org.hibernate.boot.AttributeConverterInfo; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.archive.internal.ByteArrayInputStreamAccess; import org.hibernate.boot.archive.scan.internal.ClassDescriptorImpl; @@ -132,6 +133,45 @@ public class ScanningCoordinatorTest extends BaseUnitTestCase { assertManagedResourcesAfterCoordinateScanWithScanner( scanner, false ); } + @Test + @TestForIssue(jiraKey = "HHH-10778") + public void testManagedResourcesAfterCoordinateScanWithConverterScanner() { + + when( classLoaderService.classForName( "converter" ) ).thenReturn( (Class) IntegerToVarcharConverter.class ); + + final Scanner scanner = (ScanEnvironment environment, ScanOptions options, ScanParameters parameters) -> { + final InputStreamAccess dummyInputStreamAccess = new ByteArrayInputStreamAccess( "dummy", new byte[0] ); + + return new ScanResultImpl( + Collections.singleton( new PackageDescriptorImpl( "dummy", dummyInputStreamAccess ) ), + Collections.singleton( new ClassDescriptorImpl( + "converter", + ClassDescriptor.Categorization.CONVERTER, + dummyInputStreamAccess + ) ), + Collections.singleton( new MappingFileDescriptorImpl( "dummy", dummyInputStreamAccess ) ) + ); + }; + + when( bootstrapContext.getScanner() ).thenReturn( scanner ); + + final ManagedResourcesImpl managedResources = ManagedResourcesImpl.baseline( + new MetadataSources(), + bootstrapContext + ); + + ScanningCoordinator.INSTANCE.coordinateScan( managedResources, bootstrapContext, xmlMappingBinderAccess ); + + assertEquals( 1, scanEnvironment.getExplicitlyListedClassNames().size() ); + assertEquals( "a.b.C", scanEnvironment.getExplicitlyListedClassNames().get( 0 ) ); + + assertEquals( 1, managedResources.getAttributeConverterDefinitions().size() ); + AttributeConverterInfo attributeConverterInfo = managedResources.getAttributeConverterDefinitions() + .iterator() + .next(); + assertEquals( IntegerToVarcharConverter.class, attributeConverterInfo.getConverterClass() ); + } + /** * Run coordinateScan() with the given Scanner and assert the emptiness * of ManagedResources. diff --git a/hibernate-core/src/test/java/org/hibernate/test/converter/AttributeConverterTest.java b/hibernate-core/src/test/java/org/hibernate/test/converter/AttributeConverterTest.java index 3bf169f93d..f052d52cbe 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/converter/AttributeConverterTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/converter/AttributeConverterTest.java @@ -45,6 +45,7 @@ import org.hibernate.type.descriptor.java.StringTypeDescriptor; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.boot.MetadataBuildingContextTestingImpl; import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.hibernate.testing.util.ExceptionUtil; import org.junit.Test; import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; @@ -67,8 +68,7 @@ public class AttributeConverterTest extends BaseUnitTestCase { fail( "expecting an exception" ); } catch (AnnotationException e) { - assertNotNull( e.getCause() ); - assertTyping( BlewUpException.class, e.getCause() ); + assertTyping( BlewUpException.class, ExceptionUtil.rootCause( e ) ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/converter/PackagePrivateAttributeConverterEntityManagerFactoryTest.java b/hibernate-core/src/test/java/org/hibernate/test/converter/PackagePrivateAttributeConverterEntityManagerFactoryTest.java new file mode 100644 index 0000000000..67a2ae9780 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/converter/PackagePrivateAttributeConverterEntityManagerFactoryTest.java @@ -0,0 +1,105 @@ +/* + * 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.converter; + +import java.util.Arrays; +import java.util.List; +import javax.persistence.AttributeConverter; +import javax.persistence.Convert; +import javax.persistence.Converter; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Tuple; + +import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; + +/** + * @author Vlad Mihalcea + */ +@TestForIssue( jiraKey = "HHH-10778" ) +public class PackagePrivateAttributeConverterEntityManagerFactoryTest extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Tester.class }; + } + + @Test + public void test() { + doInJPA( this::entityManagerFactory, entityManager -> { + Tester tester = new Tester(); + tester.setId( 1L ); + tester.setCode( 123 ); + + entityManager.persist( tester ); + } ); + + doInJPA( this::entityManagerFactory, entityManager -> { + Tuple tuple = (Tuple) entityManager.createNativeQuery( + "select code " + + "from Tester " + + "where id = :id", Tuple.class ) + .setParameter( "id", 1L ) + .getSingleResult(); + + assertEquals( "123", tuple.get( "code" ) ); + + Tester tester = entityManager.find( Tester.class, 1L ); + + assertEquals( 123, (int) tester.getCode() ); + } ); + } + + // Entity declarations used in the test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Entity(name = "Tester") + public static class Tester { + @Id + private Long id; + + @Convert( converter = IntegerToVarcharConverter.class ) + private Integer code; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + } + + @Converter( autoApply = true ) + static class IntegerToVarcharConverter implements AttributeConverter { + @Override + public String convertToDatabaseColumn(Integer attribute) { + return attribute == null ? null : attribute.toString(); + } + + @Override + public Integer convertToEntityAttribute(String dbData) { + return dbData == null ? null : Integer.valueOf( dbData ); + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/converter/PackagePrivateAttributeConverterSessionFactoryTest.java b/hibernate-core/src/test/java/org/hibernate/test/converter/PackagePrivateAttributeConverterSessionFactoryTest.java new file mode 100644 index 0000000000..60ce33fcbc --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/converter/PackagePrivateAttributeConverterSessionFactoryTest.java @@ -0,0 +1,99 @@ +/* + * 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.converter; + +import javax.persistence.AttributeConverter; +import javax.persistence.Convert; +import javax.persistence.Converter; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Tuple; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; + +/** + * @author Vlad Mihalcea + */ +@TestForIssue( jiraKey = "HHH-10778" ) +public class PackagePrivateAttributeConverterSessionFactoryTest extends BaseNonConfigCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Tester.class }; + } + + @Test + public void test() { + doInHibernate( this::sessionFactory, session -> { + Tester tester = new Tester(); + tester.setId( 1L ); + tester.setCode( 123 ); + + session.persist( tester ); + } ); + + doInHibernate( this::sessionFactory, session -> { + Tuple tuple = session.createNativeQuery( + "select code " + + "from Tester " + + "where id = :id", Tuple.class ) + .setParameter( "id", 1L ) + .getSingleResult(); + + assertEquals( "123", tuple.get( "code" ) ); + + Tester tester = session.find( Tester.class, 1L ); + + assertEquals( 123, (int) tester.getCode() ); + } ); + } + + // Entity declarations used in the test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Entity(name = "Tester") + public static class Tester { + @Id + private Long id; + + @Convert( converter = IntegerToVarcharConverter.class ) + private Integer code; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + } + + @Converter( autoApply = true ) + static class IntegerToVarcharConverter implements AttributeConverter { + @Override + public String convertToDatabaseColumn(Integer attribute) { + return attribute == null ? null : attribute.toString(); + } + + @Override + public Integer convertToEntityAttribute(String dbData) { + return dbData == null ? null : Integer.valueOf( dbData ); + } + } +}