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.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.Id;
|
||||
|
@ -73,6 +74,7 @@ public class SimpleValueBinder {
|
|||
private String defaultType = "";
|
||||
private Properties typeParameters = new Properties();
|
||||
private boolean isNationalized;
|
||||
private boolean isLob;
|
||||
|
||||
private Table table;
|
||||
private SimpleValue simpleValue;
|
||||
|
@ -134,8 +136,9 @@ public class SimpleValueBinder {
|
|||
// we cannot guess anything
|
||||
return;
|
||||
}
|
||||
|
||||
XClass returnedClassOrElement = returnedClass;
|
||||
boolean isArray = false;
|
||||
boolean isArray = false;
|
||||
if ( property.isArray() ) {
|
||||
returnedClassOrElement = property.getElementClass();
|
||||
isArray = true;
|
||||
|
@ -203,6 +206,7 @@ public class SimpleValueBinder {
|
|||
explicitType = type;
|
||||
}
|
||||
else if ( !key && property.isAnnotationPresent( Lob.class ) ) {
|
||||
isLob = true;
|
||||
if ( buildingContext.getBuildingOptions().getReflectionManager().equals( returnedClassOrElement, java.sql.Clob.class ) ) {
|
||||
type = isNationalized
|
||||
? StandardBasicTypes.NCLOB.getName()
|
||||
|
@ -247,7 +251,7 @@ public class SimpleValueBinder {
|
|||
else {
|
||||
type = "blob";
|
||||
}
|
||||
explicitType = type;
|
||||
defaultType = type;
|
||||
}
|
||||
else if ( ( !key && property.isAnnotationPresent( Enumerated.class ) )
|
||||
|| ( key && property.isAnnotationPresent( MapKeyEnumerated.class ) ) ) {
|
||||
|
@ -389,6 +393,9 @@ public class SimpleValueBinder {
|
|||
if ( isNationalized ) {
|
||||
simpleValue.makeNationalized();
|
||||
}
|
||||
if ( isLob ) {
|
||||
simpleValue.makeLob();
|
||||
}
|
||||
|
||||
linkWithValue();
|
||||
|
||||
|
@ -474,7 +481,20 @@ public class SimpleValueBinder {
|
|||
}
|
||||
|
||||
if ( persistentClassName != null || attributeConverterDefinition != null ) {
|
||||
simpleValue.setTypeUsingReflection( persistentClassName, propertyName );
|
||||
try {
|
||||
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() ) {
|
||||
|
|
|
@ -6,10 +6,13 @@
|
|||
*/
|
||||
package org.hibernate.mapping;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import javax.persistence.AttributeConverter;
|
||||
|
||||
|
@ -34,11 +37,13 @@ import org.hibernate.internal.CoreMessageLogger;
|
|||
import org.hibernate.internal.util.ReflectHelper;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.type.descriptor.JdbcTypeNameMapper;
|
||||
import org.hibernate.type.descriptor.converter.AttributeConverterSqlTypeDescriptorAdapter;
|
||||
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.LobTypeMappings;
|
||||
import org.hibernate.type.descriptor.sql.NationalizedTypeMappings;
|
||||
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorRegistry;
|
||||
|
@ -60,6 +65,7 @@ public class SimpleValue implements KeyValue {
|
|||
private String typeName;
|
||||
private Properties typeParameters;
|
||||
private boolean isNationalized;
|
||||
private boolean isLob;
|
||||
|
||||
private Properties identifierGeneratorProperties;
|
||||
private String identifierGeneratorStrategy = DEFAULT_ID_GEN_STRATEGY;
|
||||
|
@ -104,11 +110,11 @@ public class SimpleValue implements KeyValue {
|
|||
columns.add(column);
|
||||
}
|
||||
column.setValue(this);
|
||||
column.setTypeIndex( columns.size()-1 );
|
||||
column.setTypeIndex( columns.size() - 1 );
|
||||
}
|
||||
|
||||
public void addFormula(Formula formula) {
|
||||
columns.add(formula);
|
||||
columns.add( formula );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -168,6 +174,14 @@ public class SimpleValue implements KeyValue {
|
|||
return isNationalized;
|
||||
}
|
||||
|
||||
public void makeLob() {
|
||||
this.isLob = true;
|
||||
}
|
||||
|
||||
public boolean isLob() {
|
||||
return isLob;
|
||||
}
|
||||
|
||||
public void setTable(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
|
||||
// VARCHAR/CHAR
|
||||
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() ) {
|
||||
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