HHH-9055 - Binding support for IdClass and MapsId needs a complete review

This commit is contained in:
Steve Ebersole 2014-03-28 16:56:28 -05:00
parent 3cd4ae6c2a
commit 545516328a
8 changed files with 341 additions and 65 deletions

View File

@ -549,15 +549,9 @@ public class JoinWalker {
final Type idType = persister.getIdentifierType(); final Type idType = persister.getIdentifierType();
if ( idType.isComponentType() ) { if ( idType.isComponentType() ) {
final CompositeType cidType = (CompositeType) idType; final CompositeType cidType = (CompositeType) idType;
if ( cidType.isEmbedded() ) { if ( persister.getEntityMetamodel().getIdentifierProperty().isVirtual() ) {
// we have an embedded composite identifier. Most likely we need to process the composite // we have a non-aggregated composite identifier. We need to process
// properties separately, although there is an edge case where the identifier is really // the composite sub-properties separately
// a simple identifier (single value) wrapped in a JPA @IdClass or even in the case of a
// a simple identifier (single value) wrapped in a Hibernate composite type.
//
// We really do not have a built-in method to determine that. However, generally the
// persister would report that there is single, physical identifier property which is
// explicitly at odds with the notion of "embedded composite". So we use that for now
if ( persister.getEntityMetamodel().getIdentifierProperty().isEmbedded() ) { if ( persister.getEntityMetamodel().getIdentifierProperty().isEmbedded() ) {
walkComponentTree( walkComponentTree(
cidType, cidType,
@ -570,6 +564,27 @@ public class JoinWalker {
); );
} }
} }
// if ( cidType.isEmbedded() ) {
// // we have an embedded composite identifier. Most likely we need to process the composite
// // properties separately, although there is an edge case where the identifier is really
// // a simple identifier (single value) wrapped in a JPA @IdClass or even in the case of a
// // a simple identifier (single value) wrapped in a Hibernate composite type.
// //
// // We really do not have a built-in method to determine that. However, generally the
// // persister would report that there is single, physical identifier property which is
// // explicitly at odds with the notion of "embedded composite". So we use that for now
// if ( persister.getEntityMetamodel().getIdentifierProperty().isEmbedded() ) {
// walkComponentTree(
// cidType,
// -1,
// 0,
// persister,
// alias,
// path,
// currentDepth
// );
// }
// }
} }
} }

View File

@ -538,7 +538,7 @@ public class EntityBinding extends AbstractAttributeBindingContainer implements
getPathBase(), getPathBase(),
idAttributeBindings idAttributeBindings
); );
registerAttributeBinding( binding ); // registerAttributeBinding( binding );
return binding; return binding;
} }

View File

@ -48,9 +48,15 @@ import static org.hibernate.id.EntityIdentifierNature.NON_AGGREGATED_COMPOSITE;
import static org.hibernate.id.EntityIdentifierNature.SIMPLE; import static org.hibernate.id.EntityIdentifierNature.SIMPLE;
/** /**
* Hold information about the entity identifier. At a high-level, can be one of 2-types:<ul> * Hold information about the entity identifier. At a high-level, can be one of
* <li>single-attribute identifier - this includes both simple identifiers and aggregated composite identifiers</li> * 2 types:<ul>
* <li>multiple-attribute identifier - non-aggregated composite identifiers</li> * <li>
* single-attribute identifier - this includes both simple identifiers
* and aggregated composite identifiers
* </li>
* <li>
* multi-attribute identifier - non-aggregated composite identifiers
* </li>
* </ul> * </ul>
* *
* @author Steve Ebersole * @author Steve Ebersole

View File

@ -35,6 +35,7 @@ import org.hibernate.engine.spi.CascadeStyles;
import org.hibernate.engine.spi.IdentifierValue; import org.hibernate.engine.spi.IdentifierValue;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.VersionValue; import org.hibernate.engine.spi.VersionValue;
import org.hibernate.id.EntityIdentifierNature;
import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.IdentifierGenerator;
import org.hibernate.internal.util.ReflectHelper; import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.mapping.KeyValue; import org.hibernate.mapping.KeyValue;
@ -58,6 +59,7 @@ import org.hibernate.tuple.entity.EntityBasedBasicAttribute;
import org.hibernate.tuple.entity.EntityBasedCompositionAttribute; import org.hibernate.tuple.entity.EntityBasedCompositionAttribute;
import org.hibernate.tuple.entity.VersionProperty; import org.hibernate.tuple.entity.VersionProperty;
import org.hibernate.type.AssociationType; import org.hibernate.type.AssociationType;
import org.hibernate.type.ComponentType;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import org.hibernate.type.VersionType; import org.hibernate.type.VersionType;
@ -138,22 +140,21 @@ public final class PropertyFactory {
// see org.hibernate.metamodel.spi.domain.AbstractAttributeContainer.locateOrCreateVirtualAttribute() // see org.hibernate.metamodel.spi.domain.AbstractAttributeContainer.locateOrCreateVirtualAttribute()
final String mappedUnsavedValue = mappedEntity.getHierarchyDetails().getEntityIdentifier().getUnsavedValue(); final String mappedUnsavedValue = mappedEntity.getHierarchyDetails().getEntityIdentifier().getUnsavedValue();
final Type type;
if ( mappedEntity.getHierarchyDetails().getEntityIdentifier().isIdentifierMapper() ) { if ( mappedEntity.getHierarchyDetails().getEntityIdentifier().getNature()
type = sessionFactory.getTypeResolver().getTypeFactory().component( == EntityIdentifierNature.NON_AGGREGATED_COMPOSITE ) {
// the "attribute" will need to be virtual since there is no single
// attribute identifying the identifier
final ComponentType type = sessionFactory.getTypeResolver().getTypeFactory().component(
new ComponentMetamodel( new ComponentMetamodel(
sessionFactory.getServiceRegistry(), sessionFactory.getServiceRegistry(),
( (EmbeddedAttributeBinding) attributeBinding ).getEmbeddableBinding(), ( (EmbeddedAttributeBinding) attributeBinding ).getEmbeddableBinding(),
true, true,
true mappedEntity.getHierarchyDetails().getEntityIdentifier().getLookupClassBinding().definedIdClass()
) )
); );
}
else {
type = attributeBinding.getHibernateTypeDescriptor().getResolvedTypeMapping();
}
IdentifierValue unsavedValue = UnsavedValueFactory.getUnsavedIdentifierValue( final IdentifierValue unsavedValue = UnsavedValueFactory.getUnsavedIdentifierValue(
mappedUnsavedValue, mappedUnsavedValue,
getGetter( attributeBinding, sessionFactory ), getGetter( attributeBinding, sessionFactory ),
type, type,
@ -163,23 +164,32 @@ public final class PropertyFactory {
) )
); );
if ( attributeBinding.getAttribute().isSynthetic() ) {
// this is a virtual id property...
return new IdentifierProperty( return new IdentifierProperty(
type, type,
mappedEntity.getHierarchyDetails().getEntityIdentifier().isNonAggregatedComposite() && true,
mappedEntity.getHierarchyDetails().getEntityIdentifier().getIdClassClass() == null, mappedEntity.getHierarchyDetails().getEntityIdentifier().getLookupClassBinding().definedIdClass(),
mappedEntity.getHierarchyDetails().getEntityIdentifier().isIdentifierMapper(),
unsavedValue, unsavedValue,
generator generator
); );
} }
else { else {
final Type type = attributeBinding.getHibernateTypeDescriptor().getResolvedTypeMapping();
final IdentifierValue unsavedValue = UnsavedValueFactory.getUnsavedIdentifierValue(
mappedUnsavedValue,
getGetter( attributeBinding, sessionFactory ),
type,
getConstructor(
mappedEntity,
sessionFactory.getServiceRegistry().getService( ClassLoaderService.class )
)
);
return new IdentifierProperty( return new IdentifierProperty(
attributeBinding.getAttribute().getName(), attributeBinding.getAttribute().getName(),
null, null,
type, type,
mappedEntity.getHierarchyDetails().getEntityIdentifier().isNonAggregatedComposite(), false,
unsavedValue, unsavedValue,
generator generator
); );

View File

@ -46,8 +46,6 @@ import org.hibernate.id.Assigned;
import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.loader.PropertyPath; import org.hibernate.loader.PropertyPath;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.metamodel.spi.binding.AttributeBinding; import org.hibernate.metamodel.spi.binding.AttributeBinding;
import org.hibernate.metamodel.spi.binding.EntityBinding; import org.hibernate.metamodel.spi.binding.EntityBinding;
import org.hibernate.metamodel.spi.binding.EntityIdentifier; import org.hibernate.metamodel.spi.binding.EntityIdentifier;
@ -238,7 +236,10 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
@Override @Override
public Serializable getIdentifier(Object entity, SessionImplementor session) { public Serializable getIdentifier(Object entity, SessionImplementor session) {
final Object id; final Object id;
if ( entityMetamodel.getIdentifierProperty().isEmbedded() ) { if ( identifierMapperType != null ) {
id = mappedIdentifierValueMarshaller.getIdentifier( entity, getEntityMode(), session );
}
else if ( entityMetamodel.getIdentifierProperty().isEmbedded() ) {
id = entity; id = entity;
} }
else if ( HibernateProxy.class.isInstance( entity ) ) { else if ( HibernateProxy.class.isInstance( entity ) ) {
@ -246,13 +247,8 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
} }
else { else {
if ( idGetter == null ) { if ( idGetter == null ) {
if (identifierMapperType==null) {
throw new HibernateException( "The class has no identifier property: " + getEntityName() ); throw new HibernateException( "The class has no identifier property: " + getEntityName() );
} }
else {
id = mappedIdentifierValueMarshaller.getIdentifier( entity, getEntityMode(), session );
}
}
else { else {
id = idGetter.get( entity ); id = idGetter.get( entity );
} }
@ -282,7 +278,10 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
@Override @Override
public void setIdentifier(Object entity, Serializable id, SessionImplementor session) { public void setIdentifier(Object entity, Serializable id, SessionImplementor session) {
if ( entityMetamodel.getIdentifierProperty().isEmbedded() ) { if ( identifierMapperType != null && identifierMapperType.getReturnedClass().isInstance( id ) ) {
mappedIdentifierValueMarshaller.setIdentifier( entity, id, getEntityMode(), session );
}
else if ( entityMetamodel.getIdentifierProperty().isEmbedded() ) {
if ( entity != id ) { if ( entity != id ) {
CompositeType copier = (CompositeType) entityMetamodel.getIdentifierProperty().getType(); CompositeType copier = (CompositeType) entityMetamodel.getIdentifierProperty().getType();
copier.setPropertyValues( entity, copier.getPropertyValues( id, getEntityMode() ), getEntityMode() ); copier.setPropertyValues( entity, copier.getPropertyValues( id, getEntityMode() ), getEntityMode() );
@ -291,8 +290,11 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
else if ( idSetter != null ) { else if ( idSetter != null ) {
idSetter.set( entity, id, getFactory() ); idSetter.set( entity, id, getFactory() );
} }
else if ( identifierMapperType != null ) { else {
mappedIdentifierValueMarshaller.setIdentifier( entity, id, getEntityMode(), session ); throw new IllegalArgumentException(
"Could not determine how to set given value [" + id
+ "] as identifier value for entity [" + getEntityName() + "]"
);
} }
} }

View File

@ -0,0 +1,55 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2014, 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.metamodel.spi.binding;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertNotNull;
/**
* @author Steve Ebersole
*/
public class BindingHelper {
/**
* Help to get an attribute binding that we are fully expecting to exist.
* <p/>
* Helpful because it validates that the attribute exists and manages checking the
* specific type and casting.
*
* @param attributeContainer The container for the attribute
* @param attributeName The name of the attribute to get
* @param expectedType The specific AttributeBinding sub-type we are expecting
* @param <T> The generic representation of `expectedType`
*
* @return The typed attribute binding
*/
public static <T extends AttributeBinding> T locateAttributeBinding(
AttributeBindingContainer attributeContainer,
String attributeName,
Class<T> expectedType) {
AttributeBinding attributeBinding = attributeContainer.locateAttributeBinding( attributeName );
assertNotNull( "Could not locate attribute named " + attributeName, attributeBinding );
return assertTyping( expectedType, attributeBinding );
}
}

View File

@ -23,33 +23,40 @@
*/ */
package org.hibernate.metamodel.spi.binding.cid; package org.hibernate.metamodel.spi.binding.cid;
import javax.persistence.Column;
import javax.persistence.Embeddable; import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId; import javax.persistence.EmbeddedId;
import javax.persistence.Entity; import javax.persistence.Entity;
import org.hibernate.id.EntityIdentifierNature; import org.hibernate.id.EntityIdentifierNature;
import org.hibernate.metamodel.spi.binding.AttributeBinding;
import org.hibernate.metamodel.spi.binding.AttributeBindingContainer;
import org.hibernate.metamodel.spi.binding.BasicAttributeBinding; import org.hibernate.metamodel.spi.binding.BasicAttributeBinding;
import org.hibernate.metamodel.spi.binding.EmbeddedAttributeBinding; import org.hibernate.metamodel.spi.binding.EmbeddedAttributeBinding;
import org.hibernate.metamodel.spi.binding.EntityBinding; import org.hibernate.metamodel.spi.binding.EntityBinding;
import org.hibernate.metamodel.spi.binding.RelationalValueBinding;
import org.hibernate.metamodel.spi.binding.SingularAttributeBinding; import org.hibernate.metamodel.spi.binding.SingularAttributeBinding;
import org.hibernate.testing.junit4.BaseAnnotationBindingTestCase; import org.hibernate.testing.junit4.BaseAnnotationBindingTestCase;
import org.hibernate.testing.junit4.Resources; import org.hibernate.testing.junit4.Resources;
import org.junit.Test; import org.junit.Test;
import static org.hibernate.metamodel.spi.binding.BindingHelper.locateAttributeBinding;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
/** /**
* Assertions about the metamodel interpretations of basic {@link javax.persistence.EmbeddedId} usage.
*
* @author Steve Ebersole * @author Steve Ebersole
*
* @see org.hibernate.metamodel.spi.binding.cid.BasicIdClassTest
*/ */
public class BasicEmbeddedIdTest extends BaseAnnotationBindingTestCase { public class BasicEmbeddedIdTest extends BaseAnnotationBindingTestCase {
@Embeddable @Embeddable
public static class CoursePK { public static class CoursePK {
@Column( name = "dept" )
public String department; public String department;
public String code; public String code;
} }
@ -64,30 +71,61 @@ public class BasicEmbeddedIdTest extends BaseAnnotationBindingTestCase {
@Test @Test
@Resources( annotatedClasses = {CoursePK.class, Course.class} ) @Resources( annotatedClasses = {CoursePK.class, Course.class} )
public void testBasicUsage() { public void testBasicUsage() {
// get the Course entity binding
EntityBinding courseBinding = getEntityBinding( Course.class ); EntityBinding courseBinding = getEntityBinding( Course.class );
assertEquals( 2, courseBinding.getAttributeBindingClosureSpan() ); assertNotNull( courseBinding );
EmbeddedAttributeBinding keyBinding = locateAttributeBinding( courseBinding, "key", EmbeddedAttributeBinding.class );
assertEquals( 2, keyBinding.getEmbeddableBinding().attributeBindingSpan() );
assertEquals( assertEquals(
EntityIdentifierNature.AGGREGATED_COMPOSITE, EntityIdentifierNature.AGGREGATED_COMPOSITE,
courseBinding.getHierarchyDetails().getEntityIdentifier().getNature() courseBinding.getHierarchyDetails().getEntityIdentifier().getNature()
); );
assertNull(
courseBinding.getHierarchyDetails().getEntityIdentifier().getLookupClassBinding().getIdClassType()
);
// Course should be interpreted as defining 2 attributes: `key` and `title`
assertEquals( 2, courseBinding.getAttributeBindingClosureSpan() );
// just make sure `title` is one of them
locateAttributeBinding( courseBinding, "title", BasicAttributeBinding.class );
// locate the attribute binding for `key` which is the EmbeddedId attribute
EmbeddedAttributeBinding keyBinding = locateAttributeBinding(
courseBinding,
"key",
EmbeddedAttributeBinding.class
);
SingularAttributeBinding identifierAttribute = courseBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding(); SingularAttributeBinding identifierAttribute = courseBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding();
// NOTE : same does '==' // NOTE : assertSame() does '=='
assertSame( keyBinding, identifierAttribute ); assertSame( keyBinding, identifierAttribute );
BasicAttributeBinding titleBinding = locateAttributeBinding( courseBinding, "title", BasicAttributeBinding.class ); // the Embeddable for `key` (CoursePK) should also define 2 attributes: `department` and `code`
} assertEquals( 2, keyBinding.getEmbeddableBinding().attributeBindingSpan() );
private <T extends AttributeBinding> T locateAttributeBinding( BasicAttributeBinding deptAttribute = locateAttributeBinding(
AttributeBindingContainer attributeContainer, keyBinding.getEmbeddableBinding(),
String attributeName, "department",
Class<T> expectedType) { BasicAttributeBinding.class
AttributeBinding attributeBinding = attributeContainer.locateAttributeBinding( attributeName ); );
assertNotNull( "Could not locate attribute named " + attributeName, attributeBinding ); assertEquals( 1, deptAttribute.getRelationalValueBindings().size() );
return assertTyping( expectedType, attributeBinding ); RelationalValueBinding deptColumnBinding = deptAttribute.getRelationalValueBindings().get( 0 );
org.hibernate.metamodel.spi.relational.Column deptColumn = assertTyping(
org.hibernate.metamodel.spi.relational.Column.class,
deptColumnBinding.getValue()
);
assertEquals( "dept", deptColumn.getColumnName().getText() );
BasicAttributeBinding codeAttribute = locateAttributeBinding(
keyBinding.getEmbeddableBinding(),
"code",
BasicAttributeBinding.class
);
assertEquals( 1, codeAttribute.getRelationalValueBindings().size() );
RelationalValueBinding codeColumnBinding = codeAttribute.getRelationalValueBindings().get( 0 );
org.hibernate.metamodel.spi.relational.Column codeColumn = assertTyping(
org.hibernate.metamodel.spi.relational.Column.class,
codeColumnBinding.getValue()
);
assertEquals( "code", codeColumn.getColumnName().getText() );
} }

View File

@ -0,0 +1,150 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2014, 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.metamodel.spi.binding.cid;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import org.hibernate.id.EntityIdentifierNature;
import org.hibernate.metamodel.spi.binding.AttributeBinding;
import org.hibernate.metamodel.spi.binding.BasicAttributeBinding;
import org.hibernate.metamodel.spi.binding.EmbeddableBinding;
import org.hibernate.metamodel.spi.binding.EmbeddedAttributeBinding;
import org.hibernate.metamodel.spi.binding.EntityBinding;
import org.hibernate.metamodel.spi.binding.RelationalValueBinding;
import org.hibernate.testing.junit4.BaseAnnotationBindingTestCase;
import org.hibernate.testing.junit4.Resources;
import org.junit.Test;
import static org.hibernate.metamodel.spi.binding.BindingHelper.locateAttributeBinding;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* Assertions about the metamodel interpretations of basic {@link javax.persistence.IdClass} usage.
*
* @author Steve Ebersole
*
* @see org.hibernate.metamodel.spi.binding.cid.BasicEmbeddedIdTest
*/
public class BasicIdClassTest extends BaseAnnotationBindingTestCase {
public static class CoursePK {
public String department;
public String code;
}
@Entity
@IdClass( CoursePK.class )
public static class Course {
@Id
@Column( name = "dept" )
public String department;
@Id
public String code;
private String title;
}
@Test
@Resources( annotatedClasses = Course.class )
public void testBasicUsage() {
// get the Course entity binding
EntityBinding courseBinding = getEntityBinding( Course.class );
assertNotNull( courseBinding );
assertEquals(
EntityIdentifierNature.NON_AGGREGATED_COMPOSITE,
courseBinding.getHierarchyDetails().getEntityIdentifier().getNature()
);
Class idClassClass = courseBinding.getHierarchyDetails().getEntityIdentifier()
.getLookupClassBinding()
.getIdClassType();
assertNotNull( idClassClass );
// Course should be interpreted as defining 3 attributes: `department`, `code` and `title`
assertEquals( 3, courseBinding.getAttributeBindingClosureSpan() );
// just make sure `title` is one of them
locateAttributeBinding( courseBinding, "title", BasicAttributeBinding.class );
BasicAttributeBinding deptAttribute = locateAttributeBinding(
courseBinding,
"department",
BasicAttributeBinding.class
);
assertEquals( 1, deptAttribute.getRelationalValueBindings().size() );
RelationalValueBinding deptColumnBinding = deptAttribute.getRelationalValueBindings().get( 0 );
org.hibernate.metamodel.spi.relational.Column deptColumn = assertTyping(
org.hibernate.metamodel.spi.relational.Column.class,
deptColumnBinding.getValue()
);
assertEquals( "dept", deptColumn.getColumnName().getText() );
BasicAttributeBinding codeAttribute = locateAttributeBinding(
courseBinding,
"code",
BasicAttributeBinding.class
);
RelationalValueBinding codeColumnBinding = codeAttribute.getRelationalValueBindings().get( 0 );
org.hibernate.metamodel.spi.relational.Column codeColumn = assertTyping(
org.hibernate.metamodel.spi.relational.Column.class,
codeColumnBinding.getValue()
);
assertEquals( "code", codeColumn.getColumnName().getText() );
assertTrue(
courseBinding.getHierarchyDetails().getEntityIdentifier().isIdentifierAttributeBinding(
deptAttribute
)
);
assertTrue(
courseBinding.getHierarchyDetails().getEntityIdentifier().isIdentifierAttributeBinding(
codeAttribute
)
);
// get the virtual (non-aggregated composite) id attribute
EmbeddedAttributeBinding identifierAttribute = (EmbeddedAttributeBinding) courseBinding.getHierarchyDetails()
.getEntityIdentifier()
.getAttributeBinding();
assertNotNull( identifierAttribute );
// NOTE : assertSame() does `==`
EmbeddableBinding virtualEmbeddable = identifierAttribute.getEmbeddableBinding();
assertEquals( 2, virtualEmbeddable.attributeBindingSpan() );
for ( AttributeBinding subAttributeBinding : virtualEmbeddable.attributeBindings() ) {
assertTrue(
subAttributeBinding == deptAttribute
|| subAttributeBinding == codeAttribute
);
}
}
}