HHH-9599 - AnnotationException occurs when applying @Nationalized and @Convert annotations to the same field

(cherry picked from commit 38c004431d)

Conflicts:
	hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java
	hibernate-core/src/main/java/org/hibernate/cfg/annotations/SimpleValueBinder.java
	hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java
This commit is contained in:
Steve Ebersole 2015-03-20 16:55:29 -05:00
parent b9aced4ab6
commit 44bc5f2f8c
4 changed files with 207 additions and 5 deletions

View File

@ -78,6 +78,8 @@ import org.jboss.logging.Logger;
public class SimpleValueBinder {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, SimpleValueBinder.class.getName());
private Mappings mappings;
private String propertyName;
private String returnedClassName;
private Ejb3Column[] columns;
@ -85,7 +87,8 @@ public class SimpleValueBinder {
private String explicitType = "";
private String defaultType = "";
private Properties typeParameters = new Properties();
private Mappings mappings;
private boolean isNationalized;
private Table table;
private SimpleValue simpleValue;
private boolean isVersion;
@ -157,7 +160,7 @@ public class SimpleValueBinder {
typeParameters.clear();
String type = BinderHelper.ANNOTATION_STRING_DEFAULT;
final boolean isNationalized = property.isAnnotationPresent( Nationalized.class )
isNationalized = property.isAnnotationPresent( Nationalized.class )
|| mappings.useNationalizedCharacterData();
Type annType = property.getAnnotation( Type.class );
@ -387,6 +390,9 @@ public class SimpleValueBinder {
table = columns[0].getTable();
}
simpleValue = new SimpleValue( mappings, table );
if ( isNationalized ) {
simpleValue.makeNationalized();
}
linkWithValue();
@ -407,7 +413,7 @@ public class SimpleValueBinder {
if ( columns[0].isNameDeferred() && !mappings.isInSecondPass() && referencedEntityName != null ) {
mappings.addSecondPass(
new PkDrivenByDefaultMapsIdSecondPass(
referencedEntityName, ( Ejb3JoinColumn[] ) columns, simpleValue
referencedEntityName, (Ejb3JoinColumn[]) columns, simpleValue
)
);
}

View File

@ -49,6 +49,7 @@ import org.hibernate.type.descriptor.converter.AttributeConverterTypeAdapter;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry;
import org.hibernate.type.descriptor.sql.JdbcTypeJavaClassMappings;
import org.hibernate.type.descriptor.sql.NationalizedTypeMappings;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorRegistry;
import org.hibernate.usertype.DynamicParameterizedType;
@ -69,13 +70,15 @@ public class SimpleValue implements KeyValue {
private final List<Selectable> columns = new ArrayList<Selectable>();
private String typeName;
private Properties typeParameters;
private boolean isNationalized;
private Properties identifierGeneratorProperties;
private String identifierGeneratorStrategy = DEFAULT_ID_GEN_STRATEGY;
private String nullValue;
private Table table;
private String foreignKeyName;
private boolean alternateUniqueKey;
private Properties typeParameters;
private boolean cascadeDeleteEnabled;
private AttributeConverterDefinition attributeConverterDefinition;
@ -136,6 +139,15 @@ public class SimpleValue implements KeyValue {
public void setTypeName(String type) {
this.typeName = type;
}
public void makeNationalized() {
this.isNationalized = true;
}
public boolean isNationalized() {
return isNationalized;
}
public void setTable(Table table) {
this.table = table;
}
@ -318,6 +330,7 @@ public class SimpleValue implements KeyValue {
if ( typeName == null ) {
throw new MappingException( "No type name" );
}
if ( typeParameters != null
&& Boolean.valueOf( typeParameters.getProperty( DynamicParameterizedType.IS_DYNAMIC ) )
&& typeParameters.get( DynamicParameterizedType.PARAMETER_TYPE ) == null ) {
@ -360,6 +373,10 @@ public class SimpleValue implements KeyValue {
throw new MappingException( "you must specify types for a dynamic entity: " + propertyName );
}
typeName = ReflectHelper.reflectedPropertyClass( className, propertyName ).getName();
// todo : to fully support isNationalized here we need do the process hinted at above
// essentially, much of the logic from #buildAttributeConverterTypeAdapter wrt resolving
// a (1) SqlTypeDescriptor, a (2) JavaTypeDescriptor and dynamically building a BasicType
// combining them.
return;
}
@ -419,7 +436,10 @@ public class SimpleValue implements KeyValue {
// corresponding to the AttributeConverter's declared "databaseColumnJavaType" (how we read that value out
// of ResultSets). See JdbcTypeJavaClassMappings for details. Again, given example, this should return
// VARCHAR/CHAR
final int jdbcTypeCode = JdbcTypeJavaClassMappings.INSTANCE.determineJdbcTypeCodeForJavaClass( databaseColumnJavaType );
int jdbcTypeCode = JdbcTypeJavaClassMappings.INSTANCE.determineJdbcTypeCodeForJavaClass( databaseColumnJavaType );
if ( isNationalized() ) {
jdbcTypeCode = NationalizedTypeMappings.INSTANCE.getCorrespondingNationalizedCode( jdbcTypeCode );
}
// find the standard SqlTypeDescriptor for that JDBC type code.
final SqlTypeDescriptor sqlTypeDescriptor = SqlTypeDescriptorRegistry.INSTANCE.getDescriptor( jdbcTypeCode );
// find the JavaTypeDescriptor representing the "intermediate database type representation". Back to the

View File

@ -0,0 +1,71 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.type.descriptor.sql;
import java.sql.Types;
import java.util.Map;
import org.hibernate.internal.util.collections.BoundedConcurrentHashMap;
import org.jboss.logging.Logger;
/**
* Manages a mapping between nationalized and non-nationalized variants of JDBC types.
*
* At the moment we only care about being able to map non-nationalized codes to the
* corresponding nationalized equivalent, so that's all we implement for now
*
* @author Steve Ebersole
*/
public class NationalizedTypeMappings {
private static final Logger log = Logger.getLogger( NationalizedTypeMappings.class );
/**
* Singleton access
*/
public static final NationalizedTypeMappings INSTANCE = new NationalizedTypeMappings();
private final Map<Integer,Integer> nationalizedCodeByNonNationalized;
public NationalizedTypeMappings() {
this.nationalizedCodeByNonNationalized = new BoundedConcurrentHashMap<Integer, Integer>();
map( Types.CHAR, Types.NCHAR );
map( Types.CLOB, Types.NCLOB );
map( Types.LONGVARCHAR, Types.LONGNVARCHAR );
map( Types.VARCHAR, Types.NVARCHAR );
}
private void map(int nonNationalizedCode, int nationalizedCode) {
nationalizedCodeByNonNationalized.put( nonNationalizedCode, nationalizedCode );
}
public int getCorrespondingNationalizedCode(int jdbcCode) {
Integer nationalizedCode = nationalizedCodeByNonNationalized.get( jdbcCode );
if ( nationalizedCode == null ) {
log.debug( "Unable to locate nationalized jdbc-code equivalent for given jdbc code : " + jdbcCode );
return jdbcCode;
}
return nationalizedCode;
}
}

View File

@ -0,0 +1,105 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.type.converter;
import java.sql.Types;
import javax.persistence.AttributeConverter;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.Nationalized;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.internal.MetadataImpl;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Test the combination of @Nationalized and @Convert
*
* @author Steve Ebersole
*/
public class AndNationalizedTests extends BaseUnitTestCase {
@Test
@TestForIssue( jiraKey = "HHH-9599")
public void basicTest() {
StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build();
try {
Metadata metadata = new MetadataSources( ssr ).addAnnotatedClass( TestEntity.class ).buildMetadata();
( (MetadataImpl) metadata ).validate();
final PersistentClass entityBinding = metadata.getEntityBinding( TestEntity.class.getName() );
assertEquals(
Types.NVARCHAR,
entityBinding.getProperty( "name" ).getType().sqlTypes( metadata )[0]
);
}
finally {
StandardServiceRegistryBuilder.destroy( ssr );
}
}
@Entity(name = "TestEntity")
@Table(name = "TestEntity")
public static class TestEntity {
@Id
public Integer id;
@Nationalized
@Convert(converter = NameConverter.class)
public Name name;
}
public static class Name {
private final String text;
public Name(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
public static class NameConverter implements AttributeConverter<Name,String> {
@Override
public String convertToDatabaseColumn(Name attribute) {
return attribute.getText();
}
@Override
public Name convertToEntityAttribute(String dbData) {
return new Name( dbData );
}
}
}