From d5cbd8fdf03a059bcd3e4cca03af9ecfddf17709 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Tue, 30 Aug 2016 18:41:25 -0500 Subject: [PATCH] HHH-8630 - random map key column generated when using @AttributeOverrides and @ElementCollection --- ...AnnotationMetadataSourceProcessorImpl.java | 4 + .../hibernate/cfg/annotations/MapBinder.java | 27 +++++- .../org/hibernate/event/spi/EventType.java | 4 +- .../BasicAttributeOverrideTest.java | 97 +++++++++++++++++++ 4 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/embeddables/attributeOverrides/BasicAttributeOverrideTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/annotations/AnnotationMetadataSourceProcessorImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/annotations/AnnotationMetadataSourceProcessorImpl.java index edd9e7ecc0..b47b0ab9e2 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/annotations/AnnotationMetadataSourceProcessorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/annotations/AnnotationMetadataSourceProcessorImpl.java @@ -13,6 +13,7 @@ import java.util.Map; import java.util.Set; import javax.persistence.AttributeConverter; import javax.persistence.Converter; +import javax.persistence.Embeddable; import javax.persistence.Entity; import javax.persistence.MappedSuperclass; @@ -120,6 +121,9 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc || xClass.isAnnotationPresent( MappedSuperclass.class ) ) { xClasses.add( xClass ); } + else if ( xClass.isAnnotationPresent( Embeddable.class ) ) { + xClasses.add( xClass ); + } else { log.debugf( "Encountered a non-categorized annotated class [%s]; ignoring", annotatedClass.getName() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java index 91fe4133a4..54c9fbcd96 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java @@ -182,10 +182,8 @@ public class MapBinder extends CollectionBinder { throw new AnnotationException( "Unable to find class: " + mapKeyType, e ); } classType = buildingContext.getMetadataCollector().getClassType( keyXClass ); - //force in case of attribute override - boolean attributeOverride = property.isAnnotationPresent( AttributeOverride.class ) - || property.isAnnotationPresent( AttributeOverrides.class ); - if ( isEmbedded || attributeOverride ) { + // force in case of attribute override naming the key + if ( isEmbedded || mappingDefinedAttributeOverrideOnMapKey( property ) ) { classType = AnnotatedClassType.EMBEDDABLE; } } @@ -308,6 +306,27 @@ public class MapBinder extends CollectionBinder { } } + private boolean mappingDefinedAttributeOverrideOnMapKey(XProperty property) { + if ( property.isAnnotationPresent( AttributeOverride.class ) ) { + return namedMapKey( property.getAnnotation( AttributeOverride.class ) ); + } + + if ( property.isAnnotationPresent( AttributeOverrides.class ) ) { + final AttributeOverrides annotations = property.getAnnotation( AttributeOverrides.class ); + for ( AttributeOverride attributeOverride : annotations.value() ) { + if ( namedMapKey( attributeOverride ) ) { + return true; + } + } + } + + return false; + } + + private boolean namedMapKey(AttributeOverride annotation) { + return annotation.name().startsWith( "key." ); + } + protected Value createFormulatedValue( Value value, Collection collection, diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/EventType.java b/hibernate-core/src/main/java/org/hibernate/event/spi/EventType.java index 0a5171204c..ea50235f93 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/EventType.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/EventType.java @@ -136,10 +136,10 @@ public final class EventType { } private final String eventName; - private final Class baseListenerInterface; + private final Class baseListenerInterface; private final int ordinal; - private EventType(String eventName, Class baseListenerInterface) { + private EventType(String eventName, Class baseListenerInterface) { this.eventName = eventName; this.baseListenerInterface = baseListenerInterface; this.ordinal = typeCounter.getAndIncrement(); diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/embeddables/attributeOverrides/BasicAttributeOverrideTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/embeddables/attributeOverrides/BasicAttributeOverrideTest.java new file mode 100644 index 0000000000..338abcbbdf --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/embeddables/attributeOverrides/BasicAttributeOverrideTest.java @@ -0,0 +1,97 @@ +/* + * 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.annotations.embeddables.attributeOverrides; + +import java.util.Map; +import java.util.UUID; +import javax.persistence.AttributeOverride; +import javax.persistence.AttributeOverrides; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Embeddable; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.MapKeyColumn; +import javax.persistence.Table; + +import org.hibernate.mapping.Collection; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.SimpleValue; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.BasicType; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; +import static org.junit.Assert.assertTrue; + +/** + * @author Steve Ebersole + */ +public class BasicAttributeOverrideTest extends BaseNonConfigCoreFunctionalTestCase { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { TypeValue.class, AggregatedTypeValue.class }; + } + + @Test + @TestForIssue( jiraKey = "HHH-8630" ) + public void testIt() { + final PersistentClass entityBinding = metadata().getEntityBinding( AggregatedTypeValue.class.getName() ); + final Property attributesBinding = entityBinding.getProperty( "attributes" ); + final org.hibernate.mapping.Map attributesMap = (org.hibernate.mapping.Map) attributesBinding.getValue(); + + final SimpleValue mapKey = assertTyping( SimpleValue.class, attributesMap.getIndex() ); + final BasicType mapKeyType = assertTyping( BasicType.class, mapKey.getType() ); + assertTrue( String.class.equals( mapKeyType.getReturnedClass() ) ); + + // let's also make sure the @MapKeyColumn got applied + assertThat( mapKey.getColumnSpan(), is(1) ); + final org.hibernate.mapping.Column mapKeyColumn = assertTyping( org.hibernate.mapping.Column .class, mapKey.getColumnIterator().next() ); + assertThat( mapKeyColumn.getName(), equalTo( "attribute_name" ) ); + } + + @Embeddable + public static class TypeValue { + String type; + + @Column(columnDefinition = "TEXT") + String value; + } + + @Entity + @Table( name = "AGG_TYPE" ) + public static class AggregatedTypeValue { + @Id + UUID id; + + @Embedded + @AttributeOverrides({ + @AttributeOverride(name = "type", column = @Column(name = "content_type")), + @AttributeOverride(name = "value", column = @Column(name = "content_value")) + }) + TypeValue content; + + @CollectionTable( name = "ATTRIBUTES" ) + @ElementCollection(fetch = FetchType.EAGER) + @MapKeyColumn(name = "attribute_name") + @AttributeOverrides({ + @AttributeOverride(name = "value.type", column = @Column(name = "attribute_type")), + @AttributeOverride(name = "value.value", column = @Column(name = "attribute_value")) + }) + Map attributes; + } +}