HHH-9042 - Envers fails with @Converter and AttributeConverter

This commit is contained in:
Steve Ebersole 2015-03-20 11:17:00 -05:00
parent 180e714b7c
commit 22730624fc
7 changed files with 270 additions and 7 deletions

View File

@ -1695,4 +1695,9 @@ public interface CoreMessageLogger extends BasicLogger {
@LogMessage(level = DEBUG)
@Message(value = "Creating pooled optimizer (lo) with [incrementSize=%s; returnClass=%s]", id = 467)
void creatingPooledLoOptimizer(int incrementSize, String name);
@LogMessage(level = WARN)
@Message(value = "Unable to interpret type [%s] as an AttributeConverter due to an exception : %s", id = 468)
void logBadHbmAttributeConverterType(String type, String exceptionMessage);
}

View File

@ -31,8 +31,10 @@ import java.util.Properties;
import javax.persistence.AttributeConverter;
import org.hibernate.FetchMode;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cfg.AttributeConverterDefinition;
import org.hibernate.cfg.AvailableSettings;
@ -44,6 +46,8 @@ import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.IdentityGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.converter.AttributeConverterSqlTypeDescriptorAdapter;
@ -55,14 +59,12 @@ import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorRegistry;
import org.hibernate.usertype.DynamicParameterizedType;
import org.jboss.logging.Logger;
/**
* Any value that maps to columns.
* @author Gavin King
*/
public class SimpleValue implements KeyValue {
private static final Logger log = Logger.getLogger( SimpleValue.class );
private static final CoreMessageLogger log = CoreLogging.messageLogger( SimpleValue.class );
public static final String DEFAULT_ID_GEN_STRATEGY = "assigned";
@ -96,6 +98,7 @@ public class SimpleValue implements KeyValue {
return metadata;
}
@Override
public boolean isCascadeDeleteEnabled() {
return cascadeDeleteEnabled;
}
@ -114,6 +117,7 @@ public class SimpleValue implements KeyValue {
columns.add(formula);
}
@Override
public boolean hasFormula() {
Iterator iter = getColumnIterator();
while ( iter.hasNext() ) {
@ -123,27 +127,51 @@ public class SimpleValue implements KeyValue {
return false;
}
@Override
public int getColumnSpan() {
return columns.size();
}
@Override
public Iterator<Selectable> getColumnIterator() {
return columns.iterator();
}
public List getConstraintColumns() {
return columns;
}
public String getTypeName() {
return typeName;
}
public void setTypeName(String type) {
this.typeName = type;
public void setTypeName(String typeName) {
if ( typeName != null && typeName.startsWith( AttributeConverterTypeAdapter.NAME_PREFIX ) ) {
final String converterClassName = typeName.substring( AttributeConverterTypeAdapter.NAME_PREFIX.length() );
final ClassLoaderService cls = getMetadata().getMetadataBuildingOptions()
.getServiceRegistry()
.getService( ClassLoaderService.class );
try {
final Class<AttributeConverter> converterClass = cls.classForName( converterClassName );
attributeConverterDefinition = new AttributeConverterDefinition( converterClass.newInstance(), false );
return;
}
catch (Exception e) {
log.logBadHbmAttributeConverterType( typeName, e.getMessage() );
}
}
this.typeName = typeName;
}
public void setTable(Table table) {
this.table = table;
}
@Override
public void createForeignKey() throws MappingException {}
@Override
public void createForeignKeyOfEntity(String entityName) {
if ( !hasFormula() && !"none".equals(getForeignKeyName())) {
ForeignKey fk = table.createForeignKey( getForeignKeyName(), getConstraintColumns(), entityName );
@ -151,6 +179,7 @@ public class SimpleValue implements KeyValue {
}
}
@Override
public IdentifierGenerator createIdentifierGenerator(
IdentifierGeneratorFactory identifierGeneratorFactory,
Dialect dialect,
@ -445,13 +474,15 @@ public class SimpleValue implements KeyValue {
// todo : cache the AttributeConverterTypeAdapter in case that AttributeConverter is applied multiple times.
final String name = String.format(
final String name = AttributeConverterTypeAdapter.NAME_PREFIX + attributeConverterDefinition.getAttributeConverter().getClass().getName();
final String description = String.format(
"BasicType adapter for AttributeConverter<%s,%s>",
entityAttributeJavaType.getSimpleName(),
databaseColumnJavaType.getSimpleName()
);
return new AttributeConverterTypeAdapter(
name,
description,
attributeConverterDefinition.getAttributeConverter(),
sqlTypeDescriptorAdapter,
entityAttributeJavaType,

View File

@ -39,7 +39,10 @@ import org.jboss.logging.Logger;
public class AttributeConverterTypeAdapter<T> extends AbstractSingleColumnStandardBasicType<T> {
private static final Logger log = Logger.getLogger( AttributeConverterTypeAdapter.class );
public static final String NAME_PREFIX = "converted::";
private final String name;
private final String description;
private final Class modelType;
private final Class jdbcType;
@ -47,6 +50,7 @@ public class AttributeConverterTypeAdapter<T> extends AbstractSingleColumnStanda
public AttributeConverterTypeAdapter(
String name,
String description,
AttributeConverter<? extends T,?> attributeConverter,
SqlTypeDescriptor sqlTypeDescriptorAdapter,
Class modelType,
@ -54,6 +58,7 @@ public class AttributeConverterTypeAdapter<T> extends AbstractSingleColumnStanda
JavaTypeDescriptor<T> entityAttributeJavaTypeDescriptor) {
super( sqlTypeDescriptorAdapter, entityAttributeJavaTypeDescriptor );
this.name = name;
this.description = description;
this.modelType = modelType;
this.jdbcType = jdbcType;
this.attributeConverter = attributeConverter;
@ -77,4 +82,9 @@ public class AttributeConverterTypeAdapter<T> extends AbstractSingleColumnStanda
public AttributeConverter<? extends T,?> getAttributeConverter() {
return attributeConverter;
}
@Override
public String toString() {
return description;
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.envers.test.entities.converter;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
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.cfg.AvailableSettings;
import org.hibernate.envers.test.AbstractEnversTest;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.junit.Assert.assertNotNull;
/**
* @author Steve Ebersole
*/
public class BasicModelingTest extends AbstractEnversTest {
@Test
@TestForIssue( jiraKey = "HHH-9042" )
public void testMetamodelBuilding() {
StandardServiceRegistry ssr = new StandardServiceRegistryBuilder()
.applySetting( AvailableSettings.HBM2DDL_AUTO, "create-drop" )
.build();
try {
Metadata metadata = new MetadataSources( ssr )
.addAttributeConverter( SexConverter.class )
.addAnnotatedClass( Person.class )
.buildMetadata();
( (MetadataImpl) metadata ).validate();
PersistentClass personBinding = metadata.getEntityBinding( Person.class.getName() );
assertNotNull( personBinding );
PersistentClass personAuditBinding = metadata.getEntityBinding( Person.class.getName() + "_AUD" );
assertNotNull( personAuditBinding );
}
finally {
StandardServiceRegistryBuilder.destroy( ssr );
}
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.envers.test.entities.converter;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.envers.Audited;
/**
* @author Steve Ebersole
*/
@Entity
@Audited
public class Person {
@Id
@GeneratedValue( generator = "increment" )
@GenericGenerator( name = "increment", strategy="increment" )
private Long id;
@Convert(converter = SexConverter.class)
private Sex sex;
}

View File

@ -0,0 +1,32 @@
/*
* 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.envers.test.entities.converter;
/**
* @author Steve Ebersole
*/
public enum Sex {
MALE,
FEMALE;
}

View File

@ -0,0 +1,68 @@
/*
* 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.envers.test.entities.converter;
import javax.persistence.AttributeConverter;
/**
* @author Steve Ebersole
*/
public class SexConverter implements AttributeConverter<Sex, String> {
@Override
public String convertToDatabaseColumn(Sex attribute) {
if (attribute == null) {
return null;
}
switch (attribute) {
case MALE: {
return "M";
}
case FEMALE: {
return "F";
}
default: {
throw new IllegalArgumentException( "Unexpected Sex model value [" + attribute + "]" );
}
}
}
@Override
public Sex convertToEntityAttribute(String dbData) {
if (dbData == null) {
return null;
}
if ( "M".equals( dbData ) ) {
return Sex.MALE;
}
else if ( "F".equals( dbData ) ) {
return Sex.FEMALE;
}
throw new IllegalArgumentException( "Unexpected Sex db value [" + dbData + "]" );
}
}