HHH-9599 - AnnotationException occurs when applying @Nationalized and @Convert annotations to the same field
This commit is contained in:
parent
8a7bc2e7c1
commit
38c004431d
|
@ -614,6 +614,9 @@ public class ModelBinder {
|
||||||
primaryTable,
|
primaryTable,
|
||||||
entityDescriptor.getIdentifier()
|
entityDescriptor.getIdentifier()
|
||||||
);
|
);
|
||||||
|
if ( mappingDocument.getBuildingOptions().useNationalizedCharacterData() ) {
|
||||||
|
keyBinding.makeNationalized();
|
||||||
|
}
|
||||||
entityDescriptor.setKey( keyBinding );
|
entityDescriptor.setKey( keyBinding );
|
||||||
keyBinding.setCascadeDeleteEnabled( entitySource.isCascadeDeleteEnabled() );
|
keyBinding.setCascadeDeleteEnabled( entitySource.isCascadeDeleteEnabled() );
|
||||||
relationalObjectBinder.bindColumns(
|
relationalObjectBinder.bindColumns(
|
||||||
|
@ -1818,6 +1821,9 @@ public class ModelBinder {
|
||||||
secondaryTable,
|
secondaryTable,
|
||||||
persistentClass.getIdentifier()
|
persistentClass.getIdentifier()
|
||||||
);
|
);
|
||||||
|
if ( mappingDocument.getBuildingOptions().useNationalizedCharacterData() ) {
|
||||||
|
keyBinding.makeNationalized();
|
||||||
|
}
|
||||||
secondaryTableJoin.setKey( keyBinding );
|
secondaryTableJoin.setKey( keyBinding );
|
||||||
|
|
||||||
keyBinding.setCascadeDeleteEnabled( secondaryTableSource.isCascadeDeleteEnabled() );
|
keyBinding.setCascadeDeleteEnabled( secondaryTableSource.isCascadeDeleteEnabled() );
|
||||||
|
@ -2799,10 +2805,14 @@ public class ModelBinder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void bindSimpleValueType(
|
private static void bindSimpleValueType(
|
||||||
MappingDocument sourceDocument,
|
MappingDocument mappingDocument,
|
||||||
HibernateTypeSource typeSource,
|
HibernateTypeSource typeSource,
|
||||||
SimpleValue simpleValue) {
|
SimpleValue simpleValue) {
|
||||||
final TypeResolution typeResolution = resolveType( sourceDocument, typeSource );
|
if ( mappingDocument.getBuildingOptions().useNationalizedCharacterData() ) {
|
||||||
|
simpleValue.makeNationalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
final TypeResolution typeResolution = resolveType( mappingDocument, typeSource );
|
||||||
if ( typeResolution == null ) {
|
if ( typeResolution == null ) {
|
||||||
// no explicit type info was found
|
// no explicit type info was found
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -88,6 +88,8 @@ public class SimpleValueBinder {
|
||||||
private String explicitType = "";
|
private String explicitType = "";
|
||||||
private String defaultType = "";
|
private String defaultType = "";
|
||||||
private Properties typeParameters = new Properties();
|
private Properties typeParameters = new Properties();
|
||||||
|
private boolean isNationalized;
|
||||||
|
|
||||||
private Table table;
|
private Table table;
|
||||||
private SimpleValue simpleValue;
|
private SimpleValue simpleValue;
|
||||||
private boolean isVersion;
|
private boolean isVersion;
|
||||||
|
@ -159,7 +161,7 @@ public class SimpleValueBinder {
|
||||||
typeParameters.clear();
|
typeParameters.clear();
|
||||||
String type = BinderHelper.ANNOTATION_STRING_DEFAULT;
|
String type = BinderHelper.ANNOTATION_STRING_DEFAULT;
|
||||||
|
|
||||||
final boolean isNationalized = property.isAnnotationPresent( Nationalized.class )
|
isNationalized = property.isAnnotationPresent( Nationalized.class )
|
||||||
|| buildingContext.getBuildingOptions().useNationalizedCharacterData();
|
|| buildingContext.getBuildingOptions().useNationalizedCharacterData();
|
||||||
|
|
||||||
Type annType = property.getAnnotation( Type.class );
|
Type annType = property.getAnnotation( Type.class );
|
||||||
|
@ -389,6 +391,9 @@ public class SimpleValueBinder {
|
||||||
table = columns[0].getTable();
|
table = columns[0].getTable();
|
||||||
}
|
}
|
||||||
simpleValue = new SimpleValue( buildingContext.getMetadataCollector(), table );
|
simpleValue = new SimpleValue( buildingContext.getMetadataCollector(), table );
|
||||||
|
if ( isNationalized ) {
|
||||||
|
simpleValue.makeNationalized();
|
||||||
|
}
|
||||||
|
|
||||||
linkWithValue();
|
linkWithValue();
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ import java.util.Properties;
|
||||||
import javax.persistence.AttributeConverter;
|
import javax.persistence.AttributeConverter;
|
||||||
|
|
||||||
import org.hibernate.FetchMode;
|
import org.hibernate.FetchMode;
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.annotations.common.reflection.XProperty;
|
import org.hibernate.annotations.common.reflection.XProperty;
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||||
|
@ -55,6 +54,7 @@ import org.hibernate.type.descriptor.converter.AttributeConverterTypeAdapter;
|
||||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry;
|
import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry;
|
||||||
import org.hibernate.type.descriptor.sql.JdbcTypeJavaClassMappings;
|
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.SqlTypeDescriptor;
|
||||||
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorRegistry;
|
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorRegistry;
|
||||||
import org.hibernate.usertype.DynamicParameterizedType;
|
import org.hibernate.usertype.DynamicParameterizedType;
|
||||||
|
@ -73,13 +73,15 @@ public class SimpleValue implements KeyValue {
|
||||||
private final List<Selectable> columns = new ArrayList<Selectable>();
|
private final List<Selectable> columns = new ArrayList<Selectable>();
|
||||||
|
|
||||||
private String typeName;
|
private String typeName;
|
||||||
|
private Properties typeParameters;
|
||||||
|
private boolean isNationalized;
|
||||||
|
|
||||||
private Properties identifierGeneratorProperties;
|
private Properties identifierGeneratorProperties;
|
||||||
private String identifierGeneratorStrategy = DEFAULT_ID_GEN_STRATEGY;
|
private String identifierGeneratorStrategy = DEFAULT_ID_GEN_STRATEGY;
|
||||||
private String nullValue;
|
private String nullValue;
|
||||||
private Table table;
|
private Table table;
|
||||||
private String foreignKeyName;
|
private String foreignKeyName;
|
||||||
private boolean alternateUniqueKey;
|
private boolean alternateUniqueKey;
|
||||||
private Properties typeParameters;
|
|
||||||
private boolean cascadeDeleteEnabled;
|
private boolean cascadeDeleteEnabled;
|
||||||
|
|
||||||
private AttributeConverterDefinition attributeConverterDefinition;
|
private AttributeConverterDefinition attributeConverterDefinition;
|
||||||
|
@ -164,6 +166,14 @@ public class SimpleValue implements KeyValue {
|
||||||
this.typeName = typeName;
|
this.typeName = typeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void makeNationalized() {
|
||||||
|
this.isNationalized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNationalized() {
|
||||||
|
return isNationalized;
|
||||||
|
}
|
||||||
|
|
||||||
public void setTable(Table table) {
|
public void setTable(Table table) {
|
||||||
this.table = table;
|
this.table = table;
|
||||||
}
|
}
|
||||||
|
@ -357,6 +367,7 @@ public class SimpleValue implements KeyValue {
|
||||||
if ( typeName == null ) {
|
if ( typeName == null ) {
|
||||||
throw new MappingException( "No type name" );
|
throw new MappingException( "No type name" );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( typeParameters != null
|
if ( typeParameters != null
|
||||||
&& Boolean.valueOf( typeParameters.getProperty( DynamicParameterizedType.IS_DYNAMIC ) )
|
&& Boolean.valueOf( typeParameters.getProperty( DynamicParameterizedType.IS_DYNAMIC ) )
|
||||||
&& typeParameters.get( DynamicParameterizedType.PARAMETER_TYPE ) == null ) {
|
&& typeParameters.get( DynamicParameterizedType.PARAMETER_TYPE ) == null ) {
|
||||||
|
@ -399,6 +410,10 @@ public class SimpleValue implements KeyValue {
|
||||||
throw new MappingException( "Attribute types for a dynamic entity must be explicitly specified: " + propertyName );
|
throw new MappingException( "Attribute types for a dynamic entity must be explicitly specified: " + propertyName );
|
||||||
}
|
}
|
||||||
typeName = ReflectHelper.reflectedPropertyClass( className, propertyName ).getName();
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,7 +473,10 @@ public class SimpleValue implements KeyValue {
|
||||||
// corresponding to the AttributeConverter's declared "databaseColumnJavaType" (how we read that value out
|
// 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
|
// of ResultSets). See JdbcTypeJavaClassMappings for details. Again, given example, this should return
|
||||||
// VARCHAR/CHAR
|
// 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.
|
// find the standard SqlTypeDescriptor for that JDBC type code.
|
||||||
final SqlTypeDescriptor sqlTypeDescriptor = SqlTypeDescriptorRegistry.INSTANCE.getDescriptor( jdbcTypeCode );
|
final SqlTypeDescriptor sqlTypeDescriptor = SqlTypeDescriptorRegistry.INSTANCE.getDescriptor( jdbcTypeCode );
|
||||||
// find the JavaTypeDescriptor representing the "intermediate database type representation". Back to the
|
// find the JavaTypeDescriptor representing the "intermediate database type representation". Back to the
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue