HHH-9615 - Allow AttributeConverter on attributes marked as Lob
This commit is contained in:
parent
eaf28166d2
commit
3ac508882c
|
@ -9,6 +9,7 @@ package org.hibernate.cfg.annotations;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import javax.persistence.Enumerated;
|
import javax.persistence.Enumerated;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
|
@ -73,6 +74,7 @@ public class SimpleValueBinder {
|
||||||
private String defaultType = "";
|
private String defaultType = "";
|
||||||
private Properties typeParameters = new Properties();
|
private Properties typeParameters = new Properties();
|
||||||
private boolean isNationalized;
|
private boolean isNationalized;
|
||||||
|
private boolean isLob;
|
||||||
|
|
||||||
private Table table;
|
private Table table;
|
||||||
private SimpleValue simpleValue;
|
private SimpleValue simpleValue;
|
||||||
|
@ -134,6 +136,7 @@ public class SimpleValueBinder {
|
||||||
// we cannot guess anything
|
// we cannot guess anything
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
XClass returnedClassOrElement = returnedClass;
|
XClass returnedClassOrElement = returnedClass;
|
||||||
boolean isArray = false;
|
boolean isArray = false;
|
||||||
if ( property.isArray() ) {
|
if ( property.isArray() ) {
|
||||||
|
@ -203,6 +206,7 @@ public class SimpleValueBinder {
|
||||||
explicitType = type;
|
explicitType = type;
|
||||||
}
|
}
|
||||||
else if ( !key && property.isAnnotationPresent( Lob.class ) ) {
|
else if ( !key && property.isAnnotationPresent( Lob.class ) ) {
|
||||||
|
isLob = true;
|
||||||
if ( buildingContext.getBuildingOptions().getReflectionManager().equals( returnedClassOrElement, java.sql.Clob.class ) ) {
|
if ( buildingContext.getBuildingOptions().getReflectionManager().equals( returnedClassOrElement, java.sql.Clob.class ) ) {
|
||||||
type = isNationalized
|
type = isNationalized
|
||||||
? StandardBasicTypes.NCLOB.getName()
|
? StandardBasicTypes.NCLOB.getName()
|
||||||
|
@ -247,7 +251,7 @@ public class SimpleValueBinder {
|
||||||
else {
|
else {
|
||||||
type = "blob";
|
type = "blob";
|
||||||
}
|
}
|
||||||
explicitType = type;
|
defaultType = type;
|
||||||
}
|
}
|
||||||
else if ( ( !key && property.isAnnotationPresent( Enumerated.class ) )
|
else if ( ( !key && property.isAnnotationPresent( Enumerated.class ) )
|
||||||
|| ( key && property.isAnnotationPresent( MapKeyEnumerated.class ) ) ) {
|
|| ( key && property.isAnnotationPresent( MapKeyEnumerated.class ) ) ) {
|
||||||
|
@ -389,6 +393,9 @@ public class SimpleValueBinder {
|
||||||
if ( isNationalized ) {
|
if ( isNationalized ) {
|
||||||
simpleValue.makeNationalized();
|
simpleValue.makeNationalized();
|
||||||
}
|
}
|
||||||
|
if ( isLob ) {
|
||||||
|
simpleValue.makeLob();
|
||||||
|
}
|
||||||
|
|
||||||
linkWithValue();
|
linkWithValue();
|
||||||
|
|
||||||
|
@ -474,8 +481,21 @@ public class SimpleValueBinder {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( persistentClassName != null || attributeConverterDefinition != null ) {
|
if ( persistentClassName != null || attributeConverterDefinition != null ) {
|
||||||
|
try {
|
||||||
simpleValue.setTypeUsingReflection( persistentClassName, propertyName );
|
simpleValue.setTypeUsingReflection( persistentClassName, propertyName );
|
||||||
}
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
throw new MappingException(
|
||||||
|
String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"Unable to determine basic type mapping via reflection [%s -> %s]",
|
||||||
|
persistentClassName,
|
||||||
|
propertyName
|
||||||
|
),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( !simpleValue.isTypeSpecified() && isVersion() ) {
|
if ( !simpleValue.isTypeSpecified() && isVersion() ) {
|
||||||
simpleValue.setTypeName( "integer" );
|
simpleValue.setTypeName( "integer" );
|
||||||
|
|
|
@ -6,10 +6,13 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.mapping;
|
package org.hibernate.mapping;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.sql.Types;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import javax.persistence.AttributeConverter;
|
import javax.persistence.AttributeConverter;
|
||||||
|
|
||||||
|
@ -34,11 +37,13 @@ import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.util.ReflectHelper;
|
import org.hibernate.internal.util.ReflectHelper;
|
||||||
import org.hibernate.service.ServiceRegistry;
|
import org.hibernate.service.ServiceRegistry;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
import org.hibernate.type.descriptor.JdbcTypeNameMapper;
|
||||||
import org.hibernate.type.descriptor.converter.AttributeConverterSqlTypeDescriptorAdapter;
|
import org.hibernate.type.descriptor.converter.AttributeConverterSqlTypeDescriptorAdapter;
|
||||||
import org.hibernate.type.descriptor.converter.AttributeConverterTypeAdapter;
|
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.LobTypeMappings;
|
||||||
import org.hibernate.type.descriptor.sql.NationalizedTypeMappings;
|
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;
|
||||||
|
@ -60,6 +65,7 @@ public class SimpleValue implements KeyValue {
|
||||||
private String typeName;
|
private String typeName;
|
||||||
private Properties typeParameters;
|
private Properties typeParameters;
|
||||||
private boolean isNationalized;
|
private boolean isNationalized;
|
||||||
|
private boolean isLob;
|
||||||
|
|
||||||
private Properties identifierGeneratorProperties;
|
private Properties identifierGeneratorProperties;
|
||||||
private String identifierGeneratorStrategy = DEFAULT_ID_GEN_STRATEGY;
|
private String identifierGeneratorStrategy = DEFAULT_ID_GEN_STRATEGY;
|
||||||
|
@ -168,6 +174,14 @@ public class SimpleValue implements KeyValue {
|
||||||
return isNationalized;
|
return isNationalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void makeLob() {
|
||||||
|
this.isLob = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLob() {
|
||||||
|
return isLob;
|
||||||
|
}
|
||||||
|
|
||||||
public void setTable(Table table) {
|
public void setTable(Table table) {
|
||||||
this.table = table;
|
this.table = table;
|
||||||
}
|
}
|
||||||
|
@ -478,6 +492,26 @@ public class SimpleValue implements KeyValue {
|
||||||
// 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
|
||||||
int jdbcTypeCode = JdbcTypeJavaClassMappings.INSTANCE.determineJdbcTypeCodeForJavaClass( databaseColumnJavaType );
|
int jdbcTypeCode = JdbcTypeJavaClassMappings.INSTANCE.determineJdbcTypeCodeForJavaClass( databaseColumnJavaType );
|
||||||
|
if ( isLob() ) {
|
||||||
|
if ( LobTypeMappings.INSTANCE.hasCorrespondingLobCode( jdbcTypeCode ) ) {
|
||||||
|
jdbcTypeCode = LobTypeMappings.INSTANCE.getCorrespondingLobCode( jdbcTypeCode );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ( Serializable.class.isAssignableFrom( entityAttributeJavaType ) ) {
|
||||||
|
jdbcTypeCode = Types.BLOB;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"JDBC type-code [%s (%s)] not known to have a corresponding LOB equivalent, and Java type is not Serializable (to use BLOB)",
|
||||||
|
jdbcTypeCode,
|
||||||
|
JdbcTypeNameMapper.getTypeName( jdbcTypeCode )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if ( isNationalized() ) {
|
if ( isNationalized() ) {
|
||||||
jdbcTypeCode = NationalizedTypeMappings.INSTANCE.getCorrespondingNationalizedCode( jdbcTypeCode );
|
jdbcTypeCode = NationalizedTypeMappings.INSTANCE.getCorrespondingNationalizedCode( jdbcTypeCode );
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.type.descriptor.sql;
|
||||||
|
|
||||||
|
import java.sql.Types;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hibernate.internal.util.collections.BoundedConcurrentHashMap;
|
||||||
|
import org.hibernate.type.descriptor.JdbcTypeNameMapper;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class LobTypeMappings {
|
||||||
|
private static final Logger log = Logger.getLogger( LobTypeMappings.class );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton access
|
||||||
|
*/
|
||||||
|
public static final LobTypeMappings INSTANCE = new LobTypeMappings();
|
||||||
|
|
||||||
|
private final Map<Integer,Integer> lobCodeByNonLobCode;
|
||||||
|
|
||||||
|
private LobTypeMappings() {
|
||||||
|
this.lobCodeByNonLobCode = new BoundedConcurrentHashMap<Integer, Integer>();
|
||||||
|
|
||||||
|
// BLOB mappings
|
||||||
|
this.lobCodeByNonLobCode.put( Types.BLOB, Types.BLOB );
|
||||||
|
this.lobCodeByNonLobCode.put( Types.BINARY, Types.BLOB );
|
||||||
|
this.lobCodeByNonLobCode.put( Types.VARBINARY, Types.BLOB );
|
||||||
|
this.lobCodeByNonLobCode.put( Types.LONGVARBINARY, Types.BLOB );
|
||||||
|
|
||||||
|
// CLOB mappings
|
||||||
|
this.lobCodeByNonLobCode.put( Types.CLOB, Types.CLOB );
|
||||||
|
this.lobCodeByNonLobCode.put( Types.CHAR, Types.CLOB );
|
||||||
|
this.lobCodeByNonLobCode.put( Types.VARCHAR, Types.CLOB );
|
||||||
|
this.lobCodeByNonLobCode.put( Types.LONGVARCHAR, Types.CLOB );
|
||||||
|
|
||||||
|
// NCLOB mappings
|
||||||
|
this.lobCodeByNonLobCode.put( Types.NCLOB, Types.NCLOB );
|
||||||
|
this.lobCodeByNonLobCode.put( Types.NCHAR, Types.NCLOB );
|
||||||
|
this.lobCodeByNonLobCode.put( Types.NVARCHAR, Types.NCLOB );
|
||||||
|
this.lobCodeByNonLobCode.put( Types.LONGNVARCHAR, Types.NCLOB );
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasCorrespondingLobCode(int jdbcTypeCode) {
|
||||||
|
return lobCodeByNonLobCode.containsKey( jdbcTypeCode );
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCorrespondingLobCode(int jdbcTypeCode) {
|
||||||
|
Integer lobTypeCode = lobCodeByNonLobCode.get( jdbcTypeCode );
|
||||||
|
if ( lobTypeCode == null ) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"JDBC type-code [%s (%s)] not known to have a corresponding LOB equivalent",
|
||||||
|
jdbcTypeCode,
|
||||||
|
JdbcTypeNameMapper.getTypeName( jdbcTypeCode )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return lobTypeCode;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.test.converter;
|
||||||
|
|
||||||
|
import java.sql.Types;
|
||||||
|
import javax.persistence.AttributeConverter;
|
||||||
|
import javax.persistence.Convert;
|
||||||
|
import javax.persistence.Converter;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Lob;
|
||||||
|
|
||||||
|
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.type.Type;
|
||||||
|
import org.hibernate.type.descriptor.converter.AttributeConverterTypeAdapter;
|
||||||
|
|
||||||
|
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test mapping a model with an attribute combining {@code @Lob} with an AttributeConverter.
|
||||||
|
* <p/>
|
||||||
|
* Originally developed to diagnose HHH-9615
|
||||||
|
*
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class AndLobTest extends BaseUnitTestCase {
|
||||||
|
private StandardServiceRegistry ssr;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
ssr = new StandardServiceRegistryBuilder().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() {
|
||||||
|
if ( ssr != null ) {
|
||||||
|
StandardServiceRegistryBuilder.destroy( ssr );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMappingAttributeWithLobAndAttributeConverter() {
|
||||||
|
final Metadata metadata = new MetadataSources( ssr )
|
||||||
|
.addAnnotatedClass( EntityImpl.class )
|
||||||
|
.buildMetadata();
|
||||||
|
|
||||||
|
final Type type = metadata.getEntityBinding( EntityImpl.class.getName() ).getProperty( "status" ).getType();
|
||||||
|
final AttributeConverterTypeAdapter concreteType = assertTyping( AttributeConverterTypeAdapter.class, type );
|
||||||
|
assertEquals( Types.BLOB, concreteType.getSqlTypeDescriptor().getSqlType() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Converter
|
||||||
|
public static class ConverterImpl implements AttributeConverter<String, Integer> {
|
||||||
|
@Override
|
||||||
|
public Integer convertToDatabaseColumn(String attribute) {
|
||||||
|
return attribute.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String convertToEntityAttribute(Integer dbData) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public static class EntityImpl {
|
||||||
|
@Id
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Lob
|
||||||
|
@Convert(converter = ConverterImpl.class)
|
||||||
|
private String status;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue