HHH-6337 : Add EntityBinding methods to support single-table inheritance

This commit is contained in:
Gail Badner 2011-08-04 10:11:12 -07:00
parent eb766cc0f7
commit cb5a74fd51
2 changed files with 236 additions and 8 deletions

View File

@ -23,8 +23,10 @@
*/ */
package org.hibernate.metamodel.binding; package org.hibernate.metamodel.binding;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -32,6 +34,7 @@ import org.hibernate.AssertionFailure;
import org.hibernate.EntityMode; import org.hibernate.EntityMode;
import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.internal.util.Value; import org.hibernate.internal.util.Value;
import org.hibernate.internal.util.collections.JoinedIterable;
import org.hibernate.metamodel.domain.AttributeContainer; import org.hibernate.metamodel.domain.AttributeContainer;
import org.hibernate.metamodel.domain.Entity; import org.hibernate.metamodel.domain.Entity;
import org.hibernate.metamodel.domain.PluralAttribute; import org.hibernate.metamodel.domain.PluralAttribute;
@ -51,6 +54,7 @@ import org.hibernate.tuple.entity.EntityTuplizer;
*/ */
public class EntityBinding implements AttributeBindingContainer { public class EntityBinding implements AttributeBindingContainer {
private final EntityBinding superEntityBinding; private final EntityBinding superEntityBinding;
private final List<EntityBinding> subEntityBindings = new ArrayList<EntityBinding>();
private final HierarchyDetails hierarchyDetails; private final HierarchyDetails hierarchyDetails;
private Entity entity; private Entity entity;
@ -112,6 +116,7 @@ public class EntityBinding implements AttributeBindingContainer {
*/ */
public EntityBinding(EntityBinding superEntityBinding) { public EntityBinding(EntityBinding superEntityBinding) {
this.superEntityBinding = superEntityBinding; this.superEntityBinding = superEntityBinding;
this.superEntityBinding.subEntityBindings.add( this );
this.hierarchyDetails = superEntityBinding.getHierarchyDetails(); this.hierarchyDetails = superEntityBinding.getHierarchyDetails();
} }
@ -127,6 +132,38 @@ public class EntityBinding implements AttributeBindingContainer {
return superEntityBinding == null; return superEntityBinding == null;
} }
public boolean isPolymorphic() {
return superEntityBinding != null ||
hierarchyDetails.getEntityDiscriminator() != null ||
! subEntityBindings.isEmpty();
}
public boolean hasSubEntityBindings() {
return subEntityBindings.size() > 0;
}
public int getSubEntityBindingSpan() {
int n = subEntityBindings.size();
for ( EntityBinding subEntityBinding : subEntityBindings ) {
n += subEntityBinding.getSubEntityBindingSpan();
}
return n;
}
/**
* Iterate over subclasses in a special 'order', most derived subclasses
* first.
* @return sub-entity bindings ordered by those entity bindings that are most derived.
*/
public Iterable<EntityBinding> getSubEntityBindingClosure() {
List<Iterable<EntityBinding>> subclassIterables =
new ArrayList<Iterable<EntityBinding>>( subEntityBindings.size() + 1 );
for ( EntityBinding subEntityBinding : subEntityBindings ) {
subclassIterables.add( subEntityBinding.getSubEntityBindingClosure() );
}
subclassIterables.add( subEntityBindings );
return new JoinedIterable<EntityBinding>( subclassIterables );
}
public Entity getEntity() { public Entity getEntity() {
return entity; return entity;
} }
@ -457,9 +494,10 @@ public class EntityBinding implements AttributeBindingContainer {
* @return The number of attribute bindings * @return The number of attribute bindings
*/ */
public int getAttributeBindingClosureSpan() { public int getAttributeBindingClosureSpan() {
// TODO: fix this after HHH-6337 is fixed; for now just return size of attributeBindingMap // TODO: update account for join attribute bindings
// if this is not a root, then need to include the superclass attribute bindings return superEntityBinding != null ?
return attributeBindingMap.size(); superEntityBinding.getAttributeBindingClosureSpan() + attributeBindingMap.size() :
attributeBindingMap.size();
} }
/** /**
@ -470,8 +508,17 @@ public class EntityBinding implements AttributeBindingContainer {
* @return The attribute bindings. * @return The attribute bindings.
*/ */
public Iterable<AttributeBinding> getAttributeBindingClosure() { public Iterable<AttributeBinding> getAttributeBindingClosure() {
// TODO: fix this after HHH-6337 is fixed. for now, just return attributeBindings // TODO: update size to account for joins
// if this is not a root, then need to include the superclass attribute bindings Iterable<AttributeBinding> iterable;
return attributeBindings(); if ( superEntityBinding != null ) {
List<Iterable<AttributeBinding>> iterables = new ArrayList<Iterable<AttributeBinding>>( 2 );
iterables.add( superEntityBinding.getAttributeBindingClosure() );
iterables.add( attributeBindings() );
iterable = new JoinedIterable<AttributeBinding>( iterables );
}
else {
iterable = attributeBindings();
}
return iterable;
} }
} }

View File

@ -23,6 +23,9 @@
*/ */
package org.hibernate.metamodel.source.annotations.entity; package org.hibernate.metamodel.source.annotations.entity;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType; import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue; import javax.persistence.DiscriminatorValue;
@ -34,6 +37,7 @@ import org.junit.Test;
import org.hibernate.annotations.DiscriminatorFormula; import org.hibernate.annotations.DiscriminatorFormula;
import org.hibernate.annotations.DiscriminatorOptions; import org.hibernate.annotations.DiscriminatorOptions;
import org.hibernate.metamodel.binding.AttributeBinding;
import org.hibernate.metamodel.binding.EntityBinding; import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.binding.EntityDiscriminator; import org.hibernate.metamodel.binding.EntityDiscriminator;
import org.hibernate.metamodel.relational.DerivedValue; import org.hibernate.metamodel.relational.DerivedValue;
@ -55,13 +59,14 @@ public class InheritanceBindingTest extends BaseAnnotationBindingTestCase {
public void testNoInheritance() { public void testNoInheritance() {
EntityBinding entityBinding = getEntityBinding( SingleEntity.class ); EntityBinding entityBinding = getEntityBinding( SingleEntity.class );
assertNull( entityBinding.getHierarchyDetails().getEntityDiscriminator() ); assertNull( entityBinding.getHierarchyDetails().getEntityDiscriminator() );
assertFalse( entityBinding.isPolymorphic() );
} }
@Test @Test
@Resources(annotatedClasses = { RootOfSingleTableInheritance.class, SubclassOfSingleTableInheritance.class }) @Resources(annotatedClasses = { RootOfSingleTableInheritance.class, SubclassOfSingleTableInheritance.class })
public void testDiscriminatorValue() { public void testDiscriminatorValue() {
EntityBinding entityBinding = getEntityBinding( SubclassOfSingleTableInheritance.class ); EntityBinding entityBinding = getEntityBinding( SubclassOfSingleTableInheritance.class );
assertEquals( "Wrong discriminator value", "foo", entityBinding.getDiscriminatorMatchValue() ); assertEquals( "Wrong discriminator value", "foo1", entityBinding.getDiscriminatorMatchValue() );
} }
@Test @Test
@ -101,6 +106,169 @@ public class InheritanceBindingTest extends BaseAnnotationBindingTestCase {
assertSame( rootEntityBinding, getRootEntityBinding( RootOfSingleTableInheritance.class ) ); assertSame( rootEntityBinding, getRootEntityBinding( RootOfSingleTableInheritance.class ) );
} }
@Test
@Resources(annotatedClasses = {
SubclassOfSingleTableInheritance.class,
SingleEntity.class,
RootOfSingleTableInheritance.class,
OtherSubclassOfSingleTableInheritance.class,
SubclassOfSubclassOfSingleTableInheritance.class
})
public void testNoPolymorphism() {
EntityBinding noInheritanceEntityBinding = getEntityBinding( SingleEntity.class );
assertTrue( "SingleEntity should be a root entity", noInheritanceEntityBinding.isRoot() );
assertNull( noInheritanceEntityBinding.getSuperEntityBinding() );
assertSame( noInheritanceEntityBinding, getRootEntityBinding( SingleEntity.class ) );
assertFalse( noInheritanceEntityBinding.isPolymorphic() );
assertFalse( noInheritanceEntityBinding.hasSubEntityBindings() );
assertEquals( 0, noInheritanceEntityBinding.getSubEntityBindingSpan() );
assertFalse( noInheritanceEntityBinding.getSubEntityBindingClosure().iterator().hasNext() );
assertEquals( 1, noInheritanceEntityBinding.getAttributeBindingClosureSpan() );
for ( AttributeBinding attributeBinding : noInheritanceEntityBinding.getAttributeBindingClosure() ) {
if ( attributeBinding == noInheritanceEntityBinding.getHierarchyDetails().getEntityIdentifier().getValueBinding() ) {
continue;
}
}
}
@Test
@Resources(annotatedClasses = {
SubclassOfSingleTableInheritance.class,
SingleEntity.class,
RootOfSingleTableInheritance.class,
OtherSubclassOfSingleTableInheritance.class,
SubclassOfSubclassOfSingleTableInheritance.class
})
public void testRootPolymporhism() {
EntityBinding rootEntityBinding = getEntityBinding( RootOfSingleTableInheritance.class );
EntityBinding subclassEntityBinding = getEntityBinding( SubclassOfSingleTableInheritance.class );
EntityBinding otherSubclassEntityBinding = getEntityBinding( OtherSubclassOfSingleTableInheritance.class );
EntityBinding subclassOfSubclassEntityBinding = getEntityBinding( SubclassOfSubclassOfSingleTableInheritance.class );
assertTrue( rootEntityBinding.isRoot() );
assertNull( rootEntityBinding.getDiscriminatorMatchValue() );
assertNull( rootEntityBinding.getSuperEntityBinding() );
assertSame( rootEntityBinding, getRootEntityBinding( RootOfSingleTableInheritance.class ) );
assertTrue( rootEntityBinding.isPolymorphic() );
assertTrue( rootEntityBinding.hasSubEntityBindings() );
assertEquals( 3, rootEntityBinding.getSubEntityBindingSpan() );
Set<EntityBinding> subEntityBindings = new HashSet<EntityBinding>( );
for ( EntityBinding subEntityBinding : rootEntityBinding.getSubEntityBindingClosure() ) {
subEntityBindings.add( subEntityBinding );
}
assertEquals( 3, subEntityBindings.size() );
assertTrue( subEntityBindings.contains( subclassEntityBinding ) );
assertTrue( subEntityBindings.contains( otherSubclassEntityBinding ) );
assertTrue( subEntityBindings.contains( subclassOfSubclassEntityBinding ) );
assertEquals( 1, rootEntityBinding.getAttributeBindingClosureSpan() );
Set<String> attributeNames = new HashSet<String>();
for ( AttributeBinding attributeBinding : rootEntityBinding.getAttributeBindingClosure() ) {
attributeNames.add( attributeBinding.getAttribute().getName() );
}
assertEquals( 1, attributeNames.size() );
assertTrue( attributeNames.contains( "id" ) );
}
@Test
@Resources(annotatedClasses = {
SubclassOfSingleTableInheritance.class,
SingleEntity.class,
RootOfSingleTableInheritance.class,
OtherSubclassOfSingleTableInheritance.class,
SubclassOfSubclassOfSingleTableInheritance.class
})
public void testLeafSubclassOfRoot() {
EntityBinding rootEntityBinding = getEntityBinding( RootOfSingleTableInheritance.class );
EntityBinding subclassEntityBinding = getEntityBinding( SubclassOfSingleTableInheritance.class );
EntityBinding otherSubclassEntityBinding = getEntityBinding( OtherSubclassOfSingleTableInheritance.class );
EntityBinding subclassOfSubclassEntityBinding = getEntityBinding( SubclassOfSubclassOfSingleTableInheritance.class );
assertEquals( "Wrong discriminator value", "foo2", otherSubclassEntityBinding.getDiscriminatorMatchValue() );
assertFalse( otherSubclassEntityBinding.isRoot() );
assertSame( rootEntityBinding, otherSubclassEntityBinding.getSuperEntityBinding() );
assertSame( rootEntityBinding, getRootEntityBinding( OtherSubclassOfSingleTableInheritance.class) );
assertTrue( otherSubclassEntityBinding.isPolymorphic() );
assertFalse( otherSubclassEntityBinding.hasSubEntityBindings() );
assertEquals( 0, otherSubclassEntityBinding.getSubEntityBindingSpan() );
assertFalse( otherSubclassEntityBinding.getSubEntityBindingClosure().iterator().hasNext() );
assertEquals( 2, otherSubclassEntityBinding.getAttributeBindingClosureSpan() );
Set<String> attributeNames = new HashSet<String>();
for ( AttributeBinding attributeBinding : otherSubclassEntityBinding.getAttributeBindingClosure() ) {
attributeNames.add( attributeBinding.getAttribute().getName() );
}
assertEquals( 2, attributeNames.size() );
assertTrue( attributeNames.contains( "id" ) );
assertTrue( attributeNames.contains( "otherName" ) );
}
@Test
@Resources(annotatedClasses = {
SubclassOfSingleTableInheritance.class,
SingleEntity.class,
RootOfSingleTableInheritance.class,
OtherSubclassOfSingleTableInheritance.class,
SubclassOfSubclassOfSingleTableInheritance.class
})
public void testNonLeafSubclassOfRootPolymporhism() {
EntityBinding rootEntityBinding = getEntityBinding( RootOfSingleTableInheritance.class );
EntityBinding subclassEntityBinding = getEntityBinding( SubclassOfSingleTableInheritance.class );
EntityBinding otherSubclassEntityBinding = getEntityBinding( OtherSubclassOfSingleTableInheritance.class );
EntityBinding subclassOfSubclassEntityBinding = getEntityBinding( SubclassOfSubclassOfSingleTableInheritance.class );
assertEquals( "Wrong discriminator value", "foo1", subclassEntityBinding.getDiscriminatorMatchValue() );
assertFalse( subclassEntityBinding.isRoot() );
assertSame( rootEntityBinding, subclassEntityBinding.getSuperEntityBinding() );
assertSame( rootEntityBinding, getRootEntityBinding( SubclassOfSingleTableInheritance.class ) );
assertTrue( subclassEntityBinding.isPolymorphic() );
assertTrue( subclassEntityBinding.hasSubEntityBindings() );
assertEquals( 1, subclassEntityBinding.getSubEntityBindingSpan() );
Iterator<EntityBinding> itSubEntityBindings = subclassEntityBinding.getSubEntityBindingClosure().iterator();
assertTrue( itSubEntityBindings.hasNext() );
assertSame( subclassOfSubclassEntityBinding, itSubEntityBindings.next() );
assertFalse( itSubEntityBindings.hasNext() );
assertEquals( 2, subclassEntityBinding.getAttributeBindingClosureSpan() );
Set<String> attributeNames = new HashSet<String>();
for ( AttributeBinding attributeBinding : subclassEntityBinding.getAttributeBindingClosure() ) {
attributeNames.add( attributeBinding.getAttribute().getName() );
}
assertEquals( 2, attributeNames.size() );
assertTrue( attributeNames.contains( "id" ) );
assertTrue( attributeNames.contains( "name" ) );
}
@Test
@Resources(annotatedClasses = {
SubclassOfSingleTableInheritance.class,
SingleEntity.class,
RootOfSingleTableInheritance.class,
OtherSubclassOfSingleTableInheritance.class,
SubclassOfSubclassOfSingleTableInheritance.class
})
public void testLeafSubclassOfSubclassOfRootPolymporhism() {
EntityBinding rootEntityBinding = getEntityBinding( RootOfSingleTableInheritance.class );
EntityBinding subclassEntityBinding = getEntityBinding( SubclassOfSingleTableInheritance.class );
EntityBinding otherSubclassEntityBinding = getEntityBinding( OtherSubclassOfSingleTableInheritance.class );
EntityBinding subclassOfSubclassEntityBinding = getEntityBinding( SubclassOfSubclassOfSingleTableInheritance.class );
assertEquals( "Wrong discriminator value", "foo1_1", subclassOfSubclassEntityBinding.getDiscriminatorMatchValue() );
assertFalse( subclassOfSubclassEntityBinding.isRoot() );
assertSame( subclassEntityBinding, subclassOfSubclassEntityBinding.getSuperEntityBinding() );
assertSame( rootEntityBinding, getRootEntityBinding( SubclassOfSubclassOfSingleTableInheritance.class ) );
assertTrue( subclassOfSubclassEntityBinding.isPolymorphic() );
assertFalse( subclassOfSubclassEntityBinding.hasSubEntityBindings() );
assertEquals( 0, subclassOfSubclassEntityBinding.getSubEntityBindingSpan() );
assertFalse( subclassOfSubclassEntityBinding.getSubEntityBindingClosure().iterator().hasNext() );
assertEquals( 3, subclassOfSubclassEntityBinding.getAttributeBindingClosureSpan() );
Set<String> attributeNames = new HashSet<String>();
for ( AttributeBinding attributeBinding : subclassOfSubclassEntityBinding.getAttributeBindingClosure() ) {
attributeNames.add( attributeBinding.getAttribute().getName() );
}
assertEquals( 3, attributeNames.size() );
assertTrue( attributeNames.contains( "id" ) );
assertTrue( attributeNames.contains( "name" ) );
assertTrue( attributeNames.contains( "otherOtherName" ) );
}
@Test @Test
@Resources(annotatedClasses = { RootOfSingleTableInheritance.class, SubclassOfSingleTableInheritance.class }) @Resources(annotatedClasses = { RootOfSingleTableInheritance.class, SubclassOfSingleTableInheritance.class })
public void testDefaultDiscriminatorOptions() { public void testDefaultDiscriminatorOptions() {
@ -150,8 +318,21 @@ public class InheritanceBindingTest extends BaseAnnotationBindingTestCase {
} }
@Entity @Entity
@DiscriminatorValue("foo") @DiscriminatorValue("foo1")
public class SubclassOfSingleTableInheritance extends RootOfSingleTableInheritance { public class SubclassOfSingleTableInheritance extends RootOfSingleTableInheritance {
private String name;
}
@Entity
@DiscriminatorValue("foo2")
public class OtherSubclassOfSingleTableInheritance extends RootOfSingleTableInheritance {
private String otherName;
}
@Entity
@DiscriminatorValue("foo1_1")
public class SubclassOfSubclassOfSingleTableInheritance extends SubclassOfSingleTableInheritance {
private String otherOtherName;
} }
@Entity @Entity