Skip to content

Commit ae46523

Browse files
committed
HHH-10778 - Add support for non-public AttributeConverter implementations
1 parent 1a5cee7 commit ae46523

File tree

7 files changed

+275
-4
lines changed

7 files changed

+275
-4
lines changed

hibernate-core/src/main/java/org/hibernate/cfg/AttributeConverterDefinition.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
package org.hibernate.cfg;
88

9+
import java.lang.reflect.Constructor;
910
import java.lang.reflect.ParameterizedType;
1011
import java.lang.reflect.Type;
1112
import java.lang.reflect.TypeVariable;
@@ -56,7 +57,9 @@ public static AttributeConverterDefinition from(Class<? extends AttributeConvert
5657

5758
private static AttributeConverter instantiateAttributeConverter(Class<? extends AttributeConverter> attributeConverterClass) {
5859
try {
59-
return attributeConverterClass.newInstance();
60+
Constructor<? extends AttributeConverter> constructor = attributeConverterClass.getDeclaredConstructor();
61+
constructor.setAccessible( true );
62+
return constructor.newInstance();
6063
}
6164
catch (Exception e) {
6265
throw new AnnotationException(

hibernate-core/src/main/java/org/hibernate/resource/beans/internal/FallbackBeanInstanceProducer.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
*/
77
package org.hibernate.resource.beans.internal;
88

9+
import java.lang.reflect.Constructor;
10+
911
import org.hibernate.InstantiationException;
1012
import org.hibernate.resource.beans.spi.BeanInstanceProducer;
1113

@@ -35,7 +37,9 @@ private FallbackBeanInstanceProducer() {
3537
public <B> B produceBeanInstance(Class<B> beanType) {
3638
log.tracef( "Creating ManagedBean(%s) using direct instantiation", beanType.getName() );
3739
try {
38-
return beanType.newInstance();
40+
Constructor<B> constructor = beanType.getDeclaredConstructor();
41+
constructor.setAccessible( true );
42+
return constructor.newInstance();
3943
}
4044
catch (Exception e) {
4145
throw new InstantiationException( "Could not instantiate managed bean directly", beanType, e );
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.hibernate.boot.model.process.internal;
2+
3+
import javax.persistence.AttributeConverter;
4+
import javax.persistence.Converter;
5+
6+
/**
7+
* @author Vlad Mihalcea
8+
*/
9+
@Converter( autoApply = true )
10+
class IntegerToVarcharConverter implements AttributeConverter<Integer,String> {
11+
@Override
12+
public String convertToDatabaseColumn(Integer attribute) {
13+
return attribute == null ? null : attribute.toString();
14+
}
15+
16+
@Override
17+
public Integer convertToEntityAttribute(String dbData) {
18+
return dbData == null ? null : Integer.valueOf( dbData );
19+
}
20+
}

hibernate-core/src/test/java/org/hibernate/boot/model/process/internal/ScanningCoordinatorTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.Arrays;
66
import java.util.Collections;
77

8+
import org.hibernate.boot.AttributeConverterInfo;
89
import org.hibernate.boot.MetadataSources;
910
import org.hibernate.boot.archive.internal.ByteArrayInputStreamAccess;
1011
import org.hibernate.boot.archive.scan.internal.ClassDescriptorImpl;
@@ -132,6 +133,45 @@ public ScanResult scan(final ScanEnvironment environment, final ScanOptions opti
132133
assertManagedResourcesAfterCoordinateScanWithScanner( scanner, false );
133134
}
134135

136+
@Test
137+
@TestForIssue(jiraKey = "HHH-10778")
138+
public void testManagedResourcesAfterCoordinateScanWithConverterScanner() {
139+
140+
when( classLoaderService.classForName( "converter" ) ).thenReturn( (Class) IntegerToVarcharConverter.class );
141+
142+
final Scanner scanner = (ScanEnvironment environment, ScanOptions options, ScanParameters parameters) -> {
143+
final InputStreamAccess dummyInputStreamAccess = new ByteArrayInputStreamAccess( "dummy", new byte[0] );
144+
145+
return new ScanResultImpl(
146+
Collections.singleton( new PackageDescriptorImpl( "dummy", dummyInputStreamAccess ) ),
147+
Collections.singleton( new ClassDescriptorImpl(
148+
"converter",
149+
ClassDescriptor.Categorization.CONVERTER,
150+
dummyInputStreamAccess
151+
) ),
152+
Collections.singleton( new MappingFileDescriptorImpl( "dummy", dummyInputStreamAccess ) )
153+
);
154+
};
155+
156+
when( bootstrapContext.getScanner() ).thenReturn( scanner );
157+
158+
final ManagedResourcesImpl managedResources = ManagedResourcesImpl.baseline(
159+
new MetadataSources(),
160+
bootstrapContext
161+
);
162+
163+
ScanningCoordinator.INSTANCE.coordinateScan( managedResources, bootstrapContext, xmlMappingBinderAccess );
164+
165+
assertEquals( 1, scanEnvironment.getExplicitlyListedClassNames().size() );
166+
assertEquals( "a.b.C", scanEnvironment.getExplicitlyListedClassNames().get( 0 ) );
167+
168+
assertEquals( 1, managedResources.getAttributeConverterDefinitions().size() );
169+
AttributeConverterInfo attributeConverterInfo = managedResources.getAttributeConverterDefinitions()
170+
.iterator()
171+
.next();
172+
assertEquals( IntegerToVarcharConverter.class, attributeConverterInfo.getConverterClass() );
173+
}
174+
135175
/**
136176
* Run coordinateScan() with the given Scanner and assert the emptiness
137177
* of ManagedResources.

hibernate-core/src/test/java/org/hibernate/test/converter/AttributeConverterTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.hibernate.testing.TestForIssue;
4646
import org.hibernate.testing.boot.MetadataBuildingContextTestingImpl;
4747
import org.hibernate.testing.junit4.BaseUnitTestCase;
48+
import org.hibernate.testing.util.ExceptionUtil;
4849
import org.junit.Test;
4950

5051
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
@@ -67,8 +68,7 @@ public void testErrorInstantiatingConverterClass() {
6768
fail( "expecting an exception" );
6869
}
6970
catch (AnnotationException e) {
70-
assertNotNull( e.getCause() );
71-
assertTyping( BlewUpException.class, e.getCause() );
71+
assertTyping( BlewUpException.class, ExceptionUtil.rootCause( e ) );
7272
}
7373
}
7474

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.test.converter;
8+
9+
import java.util.Arrays;
10+
import java.util.List;
11+
import javax.persistence.AttributeConverter;
12+
import javax.persistence.Convert;
13+
import javax.persistence.Converter;
14+
import javax.persistence.Entity;
15+
import javax.persistence.Id;
16+
import javax.persistence.Tuple;
17+
18+
import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor;
19+
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
20+
21+
import org.hibernate.testing.TestForIssue;
22+
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
23+
import org.junit.Test;
24+
25+
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
26+
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
27+
import static org.junit.Assert.assertEquals;
28+
29+
/**
30+
* @author Vlad Mihalcea
31+
*/
32+
@TestForIssue( jiraKey = "HHH-10778" )
33+
public class PackagePrivateAttributeConverterEntityManagerFactoryTest extends BaseEntityManagerFunctionalTestCase {
34+
35+
@Override
36+
protected Class[] getAnnotatedClasses() {
37+
return new Class[] { Tester.class };
38+
}
39+
40+
@Test
41+
public void test() {
42+
doInJPA( this::entityManagerFactory, entityManager -> {
43+
Tester tester = new Tester();
44+
tester.setId( 1L );
45+
tester.setCode( 123 );
46+
47+
entityManager.persist( tester );
48+
} );
49+
50+
doInJPA( this::entityManagerFactory, entityManager -> {
51+
Tuple tuple = (Tuple) entityManager.createNativeQuery(
52+
"select code " +
53+
"from Tester " +
54+
"where id = :id", Tuple.class )
55+
.setParameter( "id", 1L )
56+
.getSingleResult();
57+
58+
assertEquals( "123", tuple.get( "code" ) );
59+
60+
Tester tester = entityManager.find( Tester.class, 1L );
61+
62+
assertEquals( 123, (int) tester.getCode() );
63+
} );
64+
}
65+
66+
// Entity declarations used in the test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
67+
68+
@Entity(name = "Tester")
69+
public static class Tester {
70+
@Id
71+
private Long id;
72+
73+
@Convert( converter = IntegerToVarcharConverter.class )
74+
private Integer code;
75+
76+
public Long getId() {
77+
return id;
78+
}
79+
80+
public void setId(Long id) {
81+
this.id = id;
82+
}
83+
84+
public Integer getCode() {
85+
return code;
86+
}
87+
88+
public void setCode(Integer code) {
89+
this.code = code;
90+
}
91+
}
92+
93+
@Converter( autoApply = true )
94+
static class IntegerToVarcharConverter implements AttributeConverter<Integer,String> {
95+
@Override
96+
public String convertToDatabaseColumn(Integer attribute) {
97+
return attribute == null ? null : attribute.toString();
98+
}
99+
100+
@Override
101+
public Integer convertToEntityAttribute(String dbData) {
102+
return dbData == null ? null : Integer.valueOf( dbData );
103+
}
104+
}
105+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.test.converter;
8+
9+
import javax.persistence.AttributeConverter;
10+
import javax.persistence.Convert;
11+
import javax.persistence.Converter;
12+
import javax.persistence.Entity;
13+
import javax.persistence.Id;
14+
import javax.persistence.Tuple;
15+
16+
import org.hibernate.testing.TestForIssue;
17+
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
18+
import org.junit.Test;
19+
20+
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
21+
import static org.junit.Assert.assertEquals;
22+
23+
/**
24+
* @author Vlad Mihalcea
25+
*/
26+
@TestForIssue( jiraKey = "HHH-10778" )
27+
public class PackagePrivateAttributeConverterSessionFactoryTest extends BaseNonConfigCoreFunctionalTestCase {
28+
29+
@Override
30+
protected Class[] getAnnotatedClasses() {
31+
return new Class[] { Tester.class };
32+
}
33+
34+
@Test
35+
public void test() {
36+
doInHibernate( this::sessionFactory, session -> {
37+
Tester tester = new Tester();
38+
tester.setId( 1L );
39+
tester.setCode( 123 );
40+
41+
session.persist( tester );
42+
} );
43+
44+
doInHibernate( this::sessionFactory, session -> {
45+
Tuple tuple = session.createNativeQuery(
46+
"select code " +
47+
"from Tester " +
48+
"where id = :id", Tuple.class )
49+
.setParameter( "id", 1L )
50+
.getSingleResult();
51+
52+
assertEquals( "123", tuple.get( "code" ) );
53+
54+
Tester tester = session.find( Tester.class, 1L );
55+
56+
assertEquals( 123, (int) tester.getCode() );
57+
} );
58+
}
59+
60+
// Entity declarations used in the test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
61+
62+
@Entity(name = "Tester")
63+
public static class Tester {
64+
@Id
65+
private Long id;
66+
67+
@Convert( converter = IntegerToVarcharConverter.class )
68+
private Integer code;
69+
70+
public Long getId() {
71+
return id;
72+
}
73+
74+
public void setId(Long id) {
75+
this.id = id;
76+
}
77+
78+
public Integer getCode() {
79+
return code;
80+
}
81+
82+
public void setCode(Integer code) {
83+
this.code = code;
84+
}
85+
}
86+
87+
@Converter( autoApply = true )
88+
static class IntegerToVarcharConverter implements AttributeConverter<Integer,String> {
89+
@Override
90+
public String convertToDatabaseColumn(Integer attribute) {
91+
return attribute == null ? null : attribute.toString();
92+
}
93+
94+
@Override
95+
public Integer convertToEntityAttribute(String dbData) {
96+
return dbData == null ? null : Integer.valueOf( dbData );
97+
}
98+
}
99+
}

0 commit comments

Comments
 (0)