From 74a789e617a087bfc8cdb79654810106a027ee09 Mon Sep 17 00:00:00 2001 From: Vlad Mihalcea Date: Thu, 17 Nov 2016 15:27:27 +0200 Subject: [PATCH] HHH-11255 - NaturalId mapping fails when using a composite natural identifier Add support for adding unique constraints to composite entity properties, like when using a @NaturalId with an @Embedded property --- .../cfg/IndexOrUniqueKeySecondPass.java | 45 ++++++++++++++- .../CompositeNaturalIdMappingTest.java | 10 ++-- .../composite/EmbeddedNaturalIdTest.java | 56 +++++++++++++++++++ .../naturalid/composite/PostalCarrier.java | 3 + 4 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/naturalid/composite/EmbeddedNaturalIdTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/IndexOrUniqueKeySecondPass.java b/hibernate-core/src/main/java/org/hibernate/cfg/IndexOrUniqueKeySecondPass.java index 555816f459..4056d9182f 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/IndexOrUniqueKeySecondPass.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/IndexOrUniqueKeySecondPass.java @@ -6,13 +6,20 @@ */ package org.hibernate.cfg; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import org.hibernate.AnnotationException; import org.hibernate.MappingException; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.mapping.Column; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.Index; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; import org.hibernate.mapping.Table; +import org.hibernate.mapping.UniqueKey; /** * @author Emmanuel Bernard @@ -64,10 +71,27 @@ public class IndexOrUniqueKeySecondPass implements SecondPass { } if ( column != null ) { this.table = column.getTable(); - addConstraintToColumn( - buildingContext.getMetadataCollector() + + PersistentClass persistentClass = (PersistentClass) persistentClasses.get( column.getPropertyHolder().getEntityName() ); + Property property = persistentClass.getProperty( column.getPropertyName() ); + + if ( property.getValue() instanceof Component ) { + Component component = (Component) property.getValue(); + + List columns = new ArrayList<>(); + component.getColumnIterator().forEachRemaining( selectable -> { + if ( selectable instanceof Column ) { + columns.add( (Column) selectable ); + } + } ); + addConstraintToColumns( columns ); + } + else { + addConstraintToColumn( + buildingContext.getMetadataCollector() .getLogicalColumnName( table, column.getMappingColumn().getQuotedName() ) - ); + ); + } } } @@ -89,4 +113,19 @@ public class IndexOrUniqueKeySecondPass implements SecondPass { table.getOrCreateIndex( indexName ).addColumn( column ); } } + + private void addConstraintToColumns(List columns) { + if ( unique ) { + UniqueKey uniqueKey = table.getOrCreateUniqueKey( indexName ); + for ( Column column : columns ) { + uniqueKey.addColumn( column ); + } + } + else { + Index index = table.getOrCreateIndex( indexName ); + for ( Column column : columns ) { + index.addColumn( column ); + } + } + } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/naturalid/composite/CompositeNaturalIdMappingTest.java b/hibernate-core/src/test/java/org/hibernate/test/naturalid/composite/CompositeNaturalIdMappingTest.java index fdfd996ccb..45040685ef 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/naturalid/composite/CompositeNaturalIdMappingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/naturalid/composite/CompositeNaturalIdMappingTest.java @@ -12,25 +12,27 @@ import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.boot.spi.MetadataImplementor; -import org.hibernate.testing.FailureExpected; +import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseUnitTestCase; import org.junit.Test; /** * @author Steve Ebersole */ +@TestForIssue(jiraKey = "HHH-11255") public class CompositeNaturalIdMappingTest extends BaseUnitTestCase { + @Test - @FailureExpected( jiraKey = "tbd" ) public void test() { - final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build(); + final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder() + .build(); try { Metadata meta = new MetadataSources( ssr ) .addAnnotatedClass( PostalCarrier.class ) .addAnnotatedClass( PostalCode.class ) .buildMetadata(); - ( ( MetadataImplementor) meta ).validate(); + ( (MetadataImplementor) meta ).validate(); } finally { StandardServiceRegistryBuilder.destroy( ssr ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/naturalid/composite/EmbeddedNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/test/naturalid/composite/EmbeddedNaturalIdTest.java new file mode 100644 index 0000000000..c56ebc70f8 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/naturalid/composite/EmbeddedNaturalIdTest.java @@ -0,0 +1,56 @@ +/* + * 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.naturalid.composite; + +import org.hibernate.Session; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; + +/** + * @author Vlad Mihalcea + */ +@TestForIssue(jiraKey = "HHH-11255") +public class EmbeddedNaturalIdTest extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + PostalCarrier.class + }; + } + + @Test + public void test() { + doInJPA( this::entityManagerFactory, entityManager -> { + PostalCarrier postalCarrier = new PostalCarrier(); + postalCarrier.setId( 1L ); + postalCarrier.setPostalCode( new PostalCode() ); + postalCarrier.getPostalCode().setCode( "ABC123" ); + postalCarrier.getPostalCode().setCountry( "US" ); + + entityManager.persist( postalCarrier ); + } ); + doInJPA( this::entityManagerFactory, entityManager -> { + PostalCarrier postalCarrier = entityManager.unwrap( Session.class ) + .byNaturalId( PostalCarrier.class ) + .using( "postalCode", new PostalCode( "ABC123", "US" ) ) + .load(); + assertEquals( Long.valueOf( 1L ), postalCarrier.getId() ); + } ); + doInJPA( this::entityManagerFactory, entityManager -> { + PostalCarrier postalCarrier = entityManager.unwrap( Session.class ) + .bySimpleNaturalId( PostalCarrier.class ) + .load( new PostalCode( "ABC123", "US" ) ); + assertEquals( Long.valueOf( 1L ), postalCarrier.getId() ); + } ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/naturalid/composite/PostalCarrier.java b/hibernate-core/src/test/java/org/hibernate/test/naturalid/composite/PostalCarrier.java index 4e9978c687..64cabdb27f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/naturalid/composite/PostalCarrier.java +++ b/hibernate-core/src/test/java/org/hibernate/test/naturalid/composite/PostalCarrier.java @@ -24,6 +24,9 @@ public class PostalCarrier { @Embedded private PostalCode postalCode; + public PostalCarrier() { + } + public PostalCarrier(long id, PostalCode postalCode) { this.id = id; this.postalCode = postalCode;