From 22730624fc267cd12bf1e6551158fa3057fd8d58 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 20 Mar 2015 11:17:00 -0500 Subject: [PATCH] HHH-9042 - Envers fails with @Converter and AttributeConverter --- .../hibernate/internal/CoreMessageLogger.java | 5 ++ .../org/hibernate/mapping/SimpleValue.java | 45 ++++++++++-- .../AttributeConverterTypeAdapter.java | 10 +++ .../entities/converter/BasicModelingTest.java | 70 +++++++++++++++++++ .../test/entities/converter/Person.java | 47 +++++++++++++ .../envers/test/entities/converter/Sex.java | 32 +++++++++ .../test/entities/converter/SexConverter.java | 68 ++++++++++++++++++ 7 files changed, 270 insertions(+), 7 deletions(-) create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/entities/converter/BasicModelingTest.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/entities/converter/Person.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/entities/converter/Sex.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/entities/converter/SexConverter.java diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java index 6a9b0ffa6001..2f8d2794c43c 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java @@ -1695,4 +1695,9 @@ void cannotResolveNonNullableTransientDependencies(String transientEntityString, @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); + } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java b/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java index 5d4df551996d..9c11633d0bfc 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java @@ -31,8 +31,10 @@ 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.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.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 MetadataImplementor getMetadata() { return metadata; } + @Override public boolean isCascadeDeleteEnabled() { return cascadeDeleteEnabled; } @@ -113,7 +116,8 @@ public void addColumn(Column column) { public void addFormula(Formula formula) { columns.add(formula); } - + + @Override public boolean hasFormula() { Iterator iter = getColumnIterator(); while ( iter.hasNext() ) { @@ -123,27 +127,51 @@ public boolean hasFormula() { return false; } + @Override public int getColumnSpan() { return columns.size(); } + + @Override public Iterator 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 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 void createForeignKeyOfEntity(String entityName) { } } + @Override public IdentifierGenerator createIdentifierGenerator( IdentifierGeneratorFactory identifierGeneratorFactory, Dialect dialect, @@ -445,13 +474,15 @@ private Type buildAttributeConverterTypeAdapter() { // 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, diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/AttributeConverterTypeAdapter.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/AttributeConverterTypeAdapter.java index cb0ad2e23e38..636be5f5e747 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/AttributeConverterTypeAdapter.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/AttributeConverterTypeAdapter.java @@ -39,7 +39,10 @@ public class AttributeConverterTypeAdapter extends AbstractSingleColumnStandardBasicType { 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 extends AbstractSingleColumnStanda public AttributeConverterTypeAdapter( String name, + String description, AttributeConverter attributeConverter, SqlTypeDescriptor sqlTypeDescriptorAdapter, Class modelType, @@ -54,6 +58,7 @@ public AttributeConverterTypeAdapter( JavaTypeDescriptor 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 getJdbcType() { public AttributeConverter getAttributeConverter() { return attributeConverter; } + + @Override + public String toString() { + return description; + } } diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/converter/BasicModelingTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/converter/BasicModelingTest.java new file mode 100644 index 000000000000..a5cfc1f5cf21 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/converter/BasicModelingTest.java @@ -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 ); + } + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/converter/Person.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/converter/Person.java new file mode 100644 index 000000000000..1f506f1e52eb --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/converter/Person.java @@ -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; +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/converter/Sex.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/converter/Sex.java new file mode 100644 index 000000000000..1cb998973cbb --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/converter/Sex.java @@ -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; +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/converter/SexConverter.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/converter/SexConverter.java new file mode 100644 index 000000000000..46b691981edd --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/converter/SexConverter.java @@ -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 { + + @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 + "]" ); + } + +}