HHH-5764 - Support for multi-level derived ids
(cherry picked from commit a32372d751
)
This commit is contained in:
parent
b4b6fa97e6
commit
943acc78bc
|
@ -1611,7 +1611,8 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
|
|||
|
||||
processSecondPasses( pkDrivenByDefaultMapsIdSecondPassList );
|
||||
processSecondPasses( setSimpleValueTypeSecondPassList );
|
||||
processSecondPasses( copyIdentifierComponentSecondPasList );
|
||||
|
||||
processCopyIdentifierSecondPassesInOrder();
|
||||
|
||||
processFkSecondPassesInOrder();
|
||||
|
||||
|
@ -1637,6 +1638,14 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
|
|||
}
|
||||
}
|
||||
|
||||
private void processCopyIdentifierSecondPassesInOrder() {
|
||||
if ( copyIdentifierComponentSecondPasList == null ) {
|
||||
return;
|
||||
}
|
||||
sortCopyIdentifierComponentSecondPasses();
|
||||
processSecondPasses( copyIdentifierComponentSecondPasList );
|
||||
}
|
||||
|
||||
private void processSecondPasses(ArrayList<? extends SecondPass> secondPasses) {
|
||||
if ( secondPasses == null ) {
|
||||
return;
|
||||
|
@ -1649,6 +1658,40 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
|
|||
secondPasses.clear();
|
||||
}
|
||||
|
||||
private void sortCopyIdentifierComponentSecondPasses() {
|
||||
|
||||
ArrayList<CopyIdentifierComponentSecondPass> sorted =
|
||||
new ArrayList<CopyIdentifierComponentSecondPass>( copyIdentifierComponentSecondPasList.size() );
|
||||
Set<CopyIdentifierComponentSecondPass> toSort = new HashSet<CopyIdentifierComponentSecondPass>();
|
||||
toSort.addAll( copyIdentifierComponentSecondPasList );
|
||||
topologicalSort( sorted, toSort );
|
||||
copyIdentifierComponentSecondPasList = sorted;
|
||||
}
|
||||
|
||||
/* naive O(n^3) topological sort */
|
||||
private void topologicalSort( List<CopyIdentifierComponentSecondPass> sorted, Set<CopyIdentifierComponentSecondPass> toSort ) {
|
||||
while (!toSort.isEmpty()) {
|
||||
CopyIdentifierComponentSecondPass independent = null;
|
||||
|
||||
searchForIndependent:
|
||||
for ( CopyIdentifierComponentSecondPass secondPass : toSort ) {
|
||||
for ( CopyIdentifierComponentSecondPass other : toSort ) {
|
||||
if (secondPass.dependentUpon( other )) {
|
||||
continue searchForIndependent;
|
||||
}
|
||||
}
|
||||
independent = secondPass;
|
||||
break;
|
||||
}
|
||||
if (independent == null) {
|
||||
throw new MappingException( "cyclic dependency in derived identities" );
|
||||
}
|
||||
toSort.remove( independent );
|
||||
sorted.add( independent );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void processFkSecondPassesInOrder() {
|
||||
if ( fkSecondPassList == null || fkSecondPassList.isEmpty() ) {
|
||||
return;
|
||||
|
|
|
@ -10,6 +10,7 @@ import java.util.HashMap;
|
|||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
|
@ -75,23 +76,75 @@ public class CopyIdentifierComponentSecondPass implements SecondPass {
|
|||
columnByReferencedName.put( referencedColumnName.toLowerCase(Locale.ROOT), joinColumn );
|
||||
}
|
||||
//try default column orientation
|
||||
int index = 0;
|
||||
AtomicInteger index = new AtomicInteger( 0 );
|
||||
if ( columnByReferencedName.isEmpty() ) {
|
||||
isExplicitReference = false;
|
||||
for (Ejb3JoinColumn joinColumn : joinColumns) {
|
||||
columnByReferencedName.put( "" + index, joinColumn );
|
||||
index++;
|
||||
columnByReferencedName.put( "" + index.get(), joinColumn );
|
||||
index.getAndIncrement();
|
||||
}
|
||||
index = 0;
|
||||
index.set( 0 );
|
||||
}
|
||||
|
||||
while ( properties.hasNext() ) {
|
||||
Property referencedProperty = properties.next();
|
||||
if ( referencedProperty.isComposite() ) {
|
||||
throw new AssertionFailure( "Unexpected nested component on the referenced entity when mapping a @MapsId: "
|
||||
+ referencedEntityName);
|
||||
Property property = createComponentProperty( referencedPersistentClass, isExplicitReference, columnByReferencedName, index, referencedProperty );
|
||||
component.addProperty( property );
|
||||
}
|
||||
else {
|
||||
Property property = createSimpleProperty( referencedPersistentClass, isExplicitReference, columnByReferencedName, index, referencedProperty );
|
||||
component.addProperty( property );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Property createComponentProperty(
|
||||
PersistentClass referencedPersistentClass,
|
||||
boolean isExplicitReference,
|
||||
Map<String, Ejb3JoinColumn> columnByReferencedName,
|
||||
AtomicInteger index,
|
||||
Property referencedProperty ) {
|
||||
Property property = new Property();
|
||||
property.setName( referencedProperty.getName() );
|
||||
//FIXME set optional?
|
||||
//property.setOptional( property.isOptional() );
|
||||
property.setPersistentClass( component.getOwner() );
|
||||
property.setPropertyAccessorName( referencedProperty.getPropertyAccessorName() );
|
||||
Component value = new Component( buildingContext.getMetadataCollector(), component.getOwner() );
|
||||
|
||||
property.setValue( value );
|
||||
final Component referencedValue = (Component) referencedProperty.getValue();
|
||||
value.setTypeName( referencedValue.getTypeName() );
|
||||
value.setTypeParameters( referencedValue.getTypeParameters() );
|
||||
value.setComponentClassName( referencedValue.getComponentClassName() );
|
||||
|
||||
|
||||
Iterator<Property> propertyIterator = referencedValue.getPropertyIterator();
|
||||
while(propertyIterator.hasNext())
|
||||
{
|
||||
Property referencedComponentProperty = propertyIterator.next();
|
||||
|
||||
if ( referencedComponentProperty.isComposite() ) {
|
||||
Property componentProperty = createComponentProperty( referencedValue.getOwner(), isExplicitReference, columnByReferencedName, index, referencedComponentProperty );
|
||||
value.addProperty( componentProperty );
|
||||
}
|
||||
else {
|
||||
Property componentProperty = createSimpleProperty( referencedValue.getOwner(), isExplicitReference, columnByReferencedName, index, referencedComponentProperty );
|
||||
value.addProperty( componentProperty );
|
||||
}
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
|
||||
private Property createSimpleProperty(
|
||||
PersistentClass referencedPersistentClass,
|
||||
boolean isExplicitReference,
|
||||
Map<String, Ejb3JoinColumn> columnByReferencedName,
|
||||
AtomicInteger index,
|
||||
Property referencedProperty ) {
|
||||
Property property = new Property();
|
||||
property.setName( referencedProperty.getName() );
|
||||
//FIXME set optional?
|
||||
|
@ -132,8 +185,8 @@ public class CopyIdentifierComponentSecondPass implements SecondPass {
|
|||
joinColumn = columnByReferencedName.get( logicalColumnName.toLowerCase(Locale.ROOT ) );
|
||||
}
|
||||
else {
|
||||
joinColumn = columnByReferencedName.get( "" + index );
|
||||
index++;
|
||||
joinColumn = columnByReferencedName.get( "" + index.get() );
|
||||
index.getAndIncrement();
|
||||
}
|
||||
if ( joinColumn == null && ! joinColumns[0].isNameDeferred() ) {
|
||||
throw new AnnotationException(
|
||||
|
@ -145,11 +198,16 @@ public class CopyIdentifierComponentSecondPass implements SecondPass {
|
|||
final String columnName = joinColumn == null || joinColumn.isNameDeferred() ? "tata_" + column.getName() : joinColumn
|
||||
.getName();
|
||||
value.addColumn( new Column( columnName ) );
|
||||
if ( joinColumn != null ) {
|
||||
joinColumn.linkWithValue( value );
|
||||
}
|
||||
column.setValue( value );
|
||||
}
|
||||
}
|
||||
component.addProperty( property );
|
||||
}
|
||||
return property;
|
||||
}
|
||||
|
||||
public boolean dependentUpon( CopyIdentifierComponentSecondPass other ) {
|
||||
return this.referencedEntityName.equals( other.component.getOwner().getEntityName() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e3.b2;
|
||||
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MapsId;
|
||||
|
||||
@Entity
|
||||
public class Dependent {
|
||||
|
||||
@EmbeddedId
|
||||
DependentId id;
|
||||
|
||||
@MapsId("empPK")
|
||||
@ManyToOne
|
||||
Employee emp;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e3.b2;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Embeddable
|
||||
public class DependentId implements Serializable {
|
||||
String name;
|
||||
EmployeeId empPK;
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2011, 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.test.annotations.derivedidentities.e3.b2;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.test.util.SchemaUtil;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
* @author Matt Drees
|
||||
*/
|
||||
public class DerivedIdentityEmbeddedIdParentEmbeddedIdGrandparentEmbeddedIdDepTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
@Test
|
||||
public void testManyToOne() throws Exception {
|
||||
assertTrue( SchemaUtil.isColumnPresent( "Dependent", "emp_firstName", metadata() ) );
|
||||
assertTrue( SchemaUtil.isColumnPresent( "Dependent", "emp_lastName", metadata() ) );
|
||||
assertTrue( SchemaUtil.isColumnPresent( "Dependent", "name", metadata() ) );
|
||||
assertTrue( !SchemaUtil.isColumnPresent( "Dependent", "firstName", metadata() ) );
|
||||
assertTrue( !SchemaUtil.isColumnPresent( "Dependent", "lastName", metadata() ) );
|
||||
|
||||
assertTrue( SchemaUtil.isColumnPresent( "Policy", "dep_emp_firstName", metadata() ) );
|
||||
assertTrue( SchemaUtil.isColumnPresent( "Policy", "dep_emp_lastName", metadata() ) );
|
||||
assertTrue( SchemaUtil.isColumnPresent( "Policy", "type", metadata() ) );
|
||||
assertTrue( !SchemaUtil.isColumnPresent( "Policy", "firstName", metadata() ) );
|
||||
assertTrue( !SchemaUtil.isColumnPresent( "Policy", "lastName", metadata() ) );
|
||||
assertTrue( !SchemaUtil.isColumnPresent( "Policy", "name", metadata() ) );
|
||||
|
||||
|
||||
final Employee e = new Employee();
|
||||
e.empId = new EmployeeId();
|
||||
e.empId.firstName = "Emmanuel";
|
||||
e.empId.lastName = "Bernard";
|
||||
final Session s = openSession();
|
||||
s.getTransaction().begin();
|
||||
s.persist( e );
|
||||
final Dependent d = new Dependent();
|
||||
d.emp = e;
|
||||
d.id = new DependentId();
|
||||
d.id.name = "Doggy";
|
||||
s.persist( d );
|
||||
Policy p = new Policy();
|
||||
p.dep = d;
|
||||
p.id = new PolicyId();
|
||||
p.id.type = "Vet Insurance";
|
||||
s.persist( p );
|
||||
|
||||
s.flush();
|
||||
s.clear();
|
||||
p = (Policy) s.get( Policy.class, p.id );
|
||||
assertNotNull( p.dep );
|
||||
assertEquals( e.empId.firstName, p.dep.emp.empId.firstName );
|
||||
s.getTransaction().rollback();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[]{Policy.class, Dependent.class, Employee.class};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e3.b2;
|
||||
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
|
||||
|
||||
@Entity
|
||||
public class Employee {
|
||||
@EmbeddedId
|
||||
EmployeeId empId;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e3.b2;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Embeddable
|
||||
public class EmployeeId implements Serializable {
|
||||
String firstName;
|
||||
String lastName;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e3.b2;
|
||||
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MapsId;
|
||||
|
||||
@Entity
|
||||
public class Policy {
|
||||
@EmbeddedId
|
||||
PolicyId id;
|
||||
|
||||
|
||||
@MapsId("depPK")
|
||||
@ManyToOne
|
||||
Dependent dep;
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e3.b2;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Embeddable
|
||||
public class PolicyId implements Serializable {
|
||||
String type;
|
||||
DependentId depPK;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e3.b3;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Entity
|
||||
public class Dependent {
|
||||
|
||||
@EmbeddedId
|
||||
DependentId id;
|
||||
|
||||
@JoinColumns({
|
||||
@JoinColumn(name = "FIRSTNAME", referencedColumnName = "FIRSTNAME"),
|
||||
@JoinColumn(name = "LASTNAME", referencedColumnName = "lastName")
|
||||
})
|
||||
@MapsId("empPK")
|
||||
@ManyToOne
|
||||
Employee emp;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e3.b3;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Embeddable
|
||||
public class DependentId implements Serializable {
|
||||
String name;
|
||||
EmployeeId empPK;
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2011, 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.test.annotations.derivedidentities.e3.b3;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.test.util.SchemaUtil;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
* @author Matt Drees
|
||||
*/
|
||||
public class DerivedIdentityEmbeddedIdParentEmbeddedIdGrandparentEmbeddedIdColumnOverridesDepTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
@Test
|
||||
public void testManyToOne() throws Exception {
|
||||
assertTrue( SchemaUtil.isColumnPresent( "Dependent", "FIRSTNAME", metadata() ) );
|
||||
assertTrue( SchemaUtil.isColumnPresent( "Dependent", "LASTNAME", metadata() ) );
|
||||
assertTrue( SchemaUtil.isColumnPresent( "Dependent", "name", metadata() ) );
|
||||
|
||||
assertTrue( SchemaUtil.isColumnPresent( "Policy", "FIRSTNAME", metadata() ) );
|
||||
assertTrue( SchemaUtil.isColumnPresent( "Policy", "LASTNAME", metadata() ) );
|
||||
assertTrue( SchemaUtil.isColumnPresent( "Policy", "NAME", metadata() ) );
|
||||
assertTrue( SchemaUtil.isColumnPresent( "Policy", "type", metadata() ) );
|
||||
|
||||
|
||||
final Employee e = new Employee();
|
||||
e.empId = new EmployeeId();
|
||||
e.empId.firstName = "Emmanuel";
|
||||
e.empId.lastName = "Bernard";
|
||||
final Session s = openSession();
|
||||
s.getTransaction().begin();
|
||||
s.persist( e );
|
||||
final Dependent d = new Dependent();
|
||||
d.emp = e;
|
||||
d.id = new DependentId();
|
||||
d.id.name = "Doggy";
|
||||
s.persist( d );
|
||||
Policy p = new Policy();
|
||||
p.dep = d;
|
||||
p.id = new PolicyId();
|
||||
p.id.type = "Vet Insurance";
|
||||
s.persist( p );
|
||||
|
||||
s.flush();
|
||||
s.clear();
|
||||
p = (Policy) s.get( Policy.class, p.id );
|
||||
assertNotNull( p.dep );
|
||||
assertEquals( e.empId.firstName, p.dep.emp.empId.firstName );
|
||||
s.getTransaction().rollback();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[]{Policy.class, Dependent.class, Employee.class};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e3.b3;
|
||||
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
|
||||
|
||||
@Entity
|
||||
public class Employee {
|
||||
@EmbeddedId
|
||||
EmployeeId empId;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e3.b3;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Embeddable
|
||||
public class EmployeeId implements Serializable {
|
||||
String firstName;
|
||||
String lastName;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e3.b3;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Entity
|
||||
public class Policy {
|
||||
@EmbeddedId
|
||||
PolicyId id;
|
||||
|
||||
@JoinColumns({
|
||||
@JoinColumn(name = "FIRSTNAME", referencedColumnName = "FIRSTNAME"),
|
||||
@JoinColumn(name = "LASTNAME", referencedColumnName = "lastName"),
|
||||
@JoinColumn(name = "NAME", referencedColumnName = "Name")
|
||||
})
|
||||
@MapsId("depPK")
|
||||
@ManyToOne
|
||||
Dependent dep;
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e3.b3;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Embeddable
|
||||
public class PolicyId implements Serializable {
|
||||
String type;
|
||||
DependentId depPK;
|
||||
}
|
Loading…
Reference in New Issue