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
This commit is contained in:
Vlad Mihalcea 2016-11-17 15:27:27 +02:00
parent 95b1e27902
commit 74a789e617
4 changed files with 107 additions and 7 deletions

View File

@ -6,13 +6,20 @@
*/ */
package org.hibernate.cfg; package org.hibernate.cfg;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.hibernate.AnnotationException; import org.hibernate.AnnotationException;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.mapping.Column; 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.Table;
import org.hibernate.mapping.UniqueKey;
/** /**
* @author Emmanuel Bernard * @author Emmanuel Bernard
@ -64,12 +71,29 @@ public class IndexOrUniqueKeySecondPass implements SecondPass {
} }
if ( column != null ) { if ( column != null ) {
this.table = column.getTable(); this.table = column.getTable();
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<Column> columns = new ArrayList<>();
component.getColumnIterator().forEachRemaining( selectable -> {
if ( selectable instanceof Column ) {
columns.add( (Column) selectable );
}
} );
addConstraintToColumns( columns );
}
else {
addConstraintToColumn( addConstraintToColumn(
buildingContext.getMetadataCollector() buildingContext.getMetadataCollector()
.getLogicalColumnName( table, column.getMappingColumn().getQuotedName() ) .getLogicalColumnName( table, column.getMappingColumn().getQuotedName() )
); );
} }
} }
}
private void addConstraintToColumn(final String columnName ) { private void addConstraintToColumn(final String columnName ) {
Column column = table.getColumn( Column column = table.getColumn(
@ -89,4 +113,19 @@ public class IndexOrUniqueKeySecondPass implements SecondPass {
table.getOrCreateIndex( indexName ).addColumn( column ); table.getOrCreateIndex( indexName ).addColumn( column );
} }
} }
private void addConstraintToColumns(List<Column> 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 );
}
}
}
} }

View File

@ -12,25 +12,27 @@ import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.testing.FailureExpected; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseUnitTestCase; import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.Test; import org.junit.Test;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@TestForIssue(jiraKey = "HHH-11255")
public class CompositeNaturalIdMappingTest extends BaseUnitTestCase { public class CompositeNaturalIdMappingTest extends BaseUnitTestCase {
@Test @Test
@FailureExpected( jiraKey = "tbd" )
public void test() { public void test() {
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build(); final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder()
.build();
try { try {
Metadata meta = new MetadataSources( ssr ) Metadata meta = new MetadataSources( ssr )
.addAnnotatedClass( PostalCarrier.class ) .addAnnotatedClass( PostalCarrier.class )
.addAnnotatedClass( PostalCode.class ) .addAnnotatedClass( PostalCode.class )
.buildMetadata(); .buildMetadata();
( ( MetadataImplementor) meta ).validate(); ( (MetadataImplementor) meta ).validate();
} }
finally { finally {
StandardServiceRegistryBuilder.destroy( ssr ); StandardServiceRegistryBuilder.destroy( ssr );

View File

@ -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() );
} );
}
}

View File

@ -24,6 +24,9 @@ public class PostalCarrier {
@Embedded @Embedded
private PostalCode postalCode; private PostalCode postalCode;
public PostalCarrier() {
}
public PostalCarrier(long id, PostalCode postalCode) { public PostalCarrier(long id, PostalCode postalCode) {
this.id = id; this.id = id;
this.postalCode = postalCode; this.postalCode = postalCode;