HHH-4848 finish support for derived identity Yeah! More specifically @IdClass on dependent where the @IdClass points to the associated entity id type

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18708 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Emmanuel Bernard 2010-02-05 23:33:28 +00:00
parent ccad83c774
commit 8478d5b44b
10 changed files with 446 additions and 131 deletions

View File

@ -683,103 +683,63 @@ public final class
final boolean subclassAndSingleTableStrategy = inheritanceState.getType() == InheritanceType.SINGLE_TABLE
&& inheritanceState.hasParents();
//process idclass if any
Set<String> idProperties = new HashSet<String>();
XClass classWithIdClass = inheritanceState.getClassWithIdClass(false);
if ( classWithIdClass != null ) {
IdClass idClass = classWithIdClass.getAnnotation( IdClass.class );
XClass compositeClass = mappings.getReflectionManager().toXClass( idClass.value() );
boolean isComponent = true;
AccessType propertyAccessor = entityBinder.getPropertyAccessor( compositeClass );
String generatorType = "assigned";
String generator = BinderHelper.ANNOTATION_STRING_DEFAULT;
PropertyData inferredData = new PropertyPreloadedData(
entityBinder.getPropertyAccessType(), "id", compositeClass
);
PropertyData baseInferredData = new PropertyPreloadedData(
entityBinder.getPropertyAccessType(), "id", classWithIdClass
);
HashMap<String, IdGenerator> localGenerators = new HashMap<String, IdGenerator>();
boolean ignoreIdAnnotations = entityBinder.isIgnoreIdAnnotations();
entityBinder.setIgnoreIdAnnotations( true );
propertyHolder.setInIdClass( true );
bindId(
generatorType,
generator,
inferredData,
baseInferredData,
null,
propertyHolder,
localGenerators,
isComponent,
propertyAccessor, entityBinder,
true,
false,
mappings,
inheritanceStatePerClass
);
propertyHolder.setInIdClass( null );
inferredData = new PropertyPreloadedData(
propertyAccessor, "_identifierMapper", compositeClass
);
Component mapper = fillComponent(
propertyHolder,
inferredData,
baseInferredData,
propertyAccessor,
false,
entityBinder,
true,
true,
false,
mappings,
inheritanceStatePerClass
);
entityBinder.setIgnoreIdAnnotations( ignoreIdAnnotations );
persistentClass.setIdentifierMapper( mapper );
Set<String> idPropertiesIfIdClass = new HashSet<String>();
boolean isIdClass = mapAsIdClass(
inheritanceStatePerClass,
inheritanceState,
persistentClass,
entityBinder,
propertyHolder,
elementsToProcess,
idPropertiesIfIdClass,
mappings
);
//If id definition is on a mapped superclass, update the mapping
final org.hibernate.mapping.MappedSuperclass superclass = BinderHelper.getMappedSuperclassOrNull(
inferredData.getDeclaringClass(),
inheritanceStatePerClass,
mappings
);
if (superclass != null) {
superclass.setDeclaredIdentifierMapper(mapper);
}
else {
//we are for sure on the entity
persistentClass.setDeclaredIdentifierMapper( mapper );
}
Property property = new Property();
property.setName( "_identifierMapper" );
property.setNodeName( "id" );
property.setUpdateable( false );
property.setInsertable( false );
property.setValue( mapper );
property.setPropertyAccessorName( "embedded" );
persistentClass.addProperty( property );
entityBinder.setIgnoreIdAnnotations( true );
Iterator properties = mapper.getPropertyIterator();
while ( properties.hasNext() ) {
idProperties.add( ( (Property) properties.next() ).getName() );
}
}
else {
if (!isIdClass) {
entityBinder.setWrapIdsInEmbeddedComponents( elementsToProcess.getIdPropertyCount() > 1 );
}
Set<String> missingIdProperties = new HashSet<String>( idProperties );
for (PropertyData propertyAnnotatedElement : elementsToProcess.getElements() ) {
processIdPropertiesIfNotAlready(
inheritanceStatePerClass,
mappings,
persistentClass,
entityBinder,
propertyHolder,
classGenerators,
elementsToProcess,
subclassAndSingleTableStrategy,
idPropertiesIfIdClass
);
if ( !inheritanceState.hasParents() ) {
final RootClass rootClass = (RootClass) persistentClass;
mappings.addSecondPass( new CreateKeySecondPass( rootClass ) );
}
else {
superEntity.addSubclass( (Subclass) persistentClass );
}
mappings.addClass( persistentClass );
//Process secondary tables and complementary definitions (ie o.h.a.Table)
mappings.addSecondPass( new SecondaryTableSecondPass( entityBinder, propertyHolder, clazzToProcess ) );
//add process complementary Table definition (index & all)
entityBinder.processComplementaryTableDefinitions( clazzToProcess.getAnnotation( org.hibernate.annotations.Table.class ) );
entityBinder.processComplementaryTableDefinitions( clazzToProcess.getAnnotation( org.hibernate.annotations.Tables.class ) );
}
private static void processIdPropertiesIfNotAlready(Map<XClass, InheritanceState> inheritanceStatePerClass, ExtendedMappings mappings, PersistentClass persistentClass, EntityBinder entityBinder, PropertyHolder propertyHolder, HashMap<String, IdGenerator> classGenerators, InheritanceState.ElementsToProcess elementsToProcess, boolean subclassAndSingleTableStrategy, Set<String> idPropertiesIfIdClass) {
Set<String> missingIdProperties = new HashSet<String>( idPropertiesIfIdClass );
for ( PropertyData propertyAnnotatedElement : elementsToProcess.getElements() ) {
String propertyName = propertyAnnotatedElement.getPropertyName();
if ( !idProperties.contains( propertyName ) ) {
if ( !idPropertiesIfIdClass.contains( propertyName ) ) {
processElementAnnotations(
propertyHolder,
subclassAndSingleTableStrategy ?
Nullability.FORCED_NULL :
Nullability.NO_CONSTRAINT,
propertyAnnotatedElement.getProperty(),
propertyAnnotatedElement, classGenerators, entityBinder,
false, false, false, mappings, inheritanceStatePerClass
);
@ -800,24 +760,157 @@ public final class
+ ") in entity annotated with @IdClass:" + persistentClass.getEntityName()
);
}
}
if ( !inheritanceState.hasParents() ) {
final RootClass rootClass = (RootClass) persistentClass;
mappings.addSecondPass( new CreateKeySecondPass( rootClass ) );
private static boolean mapAsIdClass(Map<XClass, InheritanceState> inheritanceStatePerClass, InheritanceState inheritanceState, PersistentClass persistentClass, EntityBinder entityBinder, PropertyHolder propertyHolder, InheritanceState.ElementsToProcess elementsToProcess, Set<String> idPropertiesIfIdClass, ExtendedMappings mappings) {
/*
* We are looking for @IdClass
* In general we map the id class as identifier using the mapping metadata of the main entity's properties
* and we create an identifier mapper containing the id properties of the main entity
*
* In JPA 2, there is a shortcut if the id class is the Pk of the associated class pointed to by the id
* it ought to be treated as an embedded and not a real IdClass (at least in the Hibernate's internal way
*/
XClass classWithIdClass = inheritanceState.getClassWithIdClass(false);
if ( classWithIdClass != null ) {
IdClass idClass = classWithIdClass.getAnnotation( IdClass.class );
XClass compositeClass = mappings.getReflectionManager().toXClass( idClass.value() );
PropertyData inferredData = new PropertyPreloadedData(
entityBinder.getPropertyAccessType(), "id", compositeClass
);
PropertyData baseInferredData = new PropertyPreloadedData(
entityBinder.getPropertyAccessType(), "id", classWithIdClass
);
AccessType propertyAccessor = entityBinder.getPropertyAccessor( compositeClass );
//In JPA 2, there is a shortcut if the IdClass is the Pk of the associated class pointed to by the id
//it ought to be treated as an embedded and not a real IdClass (at least in the Hibernate's internal way
final boolean isFakeIdClass = isIdClassPkOfTheAssociatedEntity(
elementsToProcess,
compositeClass,
inferredData,
baseInferredData,
propertyAccessor,
inheritanceStatePerClass,
mappings
);
if ( isFakeIdClass ) {
return false;
}
boolean isComponent = true;
String generatorType = "assigned";
String generator = BinderHelper.ANNOTATION_STRING_DEFAULT;
boolean ignoreIdAnnotations = entityBinder.isIgnoreIdAnnotations();
entityBinder.setIgnoreIdAnnotations( true );
propertyHolder.setInIdClass( true );
bindIdClass(
generatorType,
generator,
inferredData,
baseInferredData,
null,
propertyHolder,
isComponent,
propertyAccessor,
entityBinder,
true,
false,
mappings,
inheritanceStatePerClass
);
propertyHolder.setInIdClass( null );
inferredData = new PropertyPreloadedData(
propertyAccessor, "_identifierMapper", compositeClass
);
Component mapper = fillComponent(
propertyHolder,
inferredData,
baseInferredData,
propertyAccessor,
false,
entityBinder,
true,
true,
false,
mappings,
inheritanceStatePerClass
);
entityBinder.setIgnoreIdAnnotations( ignoreIdAnnotations );
persistentClass.setIdentifierMapper( mapper );
//If id definition is on a mapped superclass, update the mapping
final org.hibernate.mapping.MappedSuperclass superclass =
BinderHelper.getMappedSuperclassOrNull(
inferredData.getDeclaringClass(),
inheritanceStatePerClass,
mappings
);
if (superclass != null) {
superclass.setDeclaredIdentifierMapper(mapper);
}
else {
//we are for sure on the entity
persistentClass.setDeclaredIdentifierMapper( mapper );
}
Property property = new Property();
property.setName( "_identifierMapper" );
property.setNodeName( "id" );
property.setUpdateable( false );
property.setInsertable( false );
property.setValue( mapper );
property.setPropertyAccessorName( "embedded" );
persistentClass.addProperty( property );
entityBinder.setIgnoreIdAnnotations( true );
Iterator properties = mapper.getPropertyIterator();
while ( properties.hasNext() ) {
idPropertiesIfIdClass.add( ( (Property) properties.next() ).getName() );
}
return true;
}
else {
superEntity.addSubclass( (Subclass) persistentClass );
return false;
}
}
mappings.addClass( persistentClass );
//Process secondary tables and complementary definitions (ie o.h.a.Table)
mappings.addSecondPass( new SecondaryTableSecondPass( entityBinder, propertyHolder, clazzToProcess ) );
//add process complementary Table definition (index & all)
entityBinder.processComplementaryTableDefinitions( clazzToProcess.getAnnotation( org.hibernate.annotations.Table.class ) );
entityBinder.processComplementaryTableDefinitions( clazzToProcess.getAnnotation( org.hibernate.annotations.Tables.class ) );
private static boolean isIdClassPkOfTheAssociatedEntity(
InheritanceState.ElementsToProcess elementsToProcess,
XClass compositeClass,
PropertyData inferredData,
PropertyData baseInferredData,
AccessType propertyAccessor,
Map<XClass, InheritanceState> inheritanceStatePerClass,
ExtendedMappings mappings) {
if ( elementsToProcess.getIdPropertyCount() == 1 ) {
final PropertyData idPropertyOnBaseClass = getUniqueIdPropertyFromBaseClass(
inferredData, baseInferredData, propertyAccessor, mappings
);
final InheritanceState state = inheritanceStatePerClass.get( idPropertyOnBaseClass.getClassOrElement() );
if (state == null) {
return false; //while it is likely a user error, let's consider it is something that might happen
}
final XClass associatedClassWithIdClass = state.getClassWithIdClass( true );
if ( associatedClassWithIdClass == null ) {
//we cannot know for sure here unless we try and find the @EmbeddedId
//Let's not do this thorough checking but do some extra validation
final XProperty property = idPropertyOnBaseClass.getProperty();
return property.isAnnotationPresent( ManyToOne.class )
|| property.isAnnotationPresent( OneToOne.class );
}
else {
final XClass idClass = mappings.getReflectionManager().toXClass(
associatedClassWithIdClass.getAnnotation( IdClass.class ).value()
);
return idClass.equals( compositeClass );
}
}
else {
return false;
}
}
private static Cache determineCacheSettings(XClass clazzToProcess, ExtendedMappings mappings) {
@ -1230,7 +1323,7 @@ public final class
* Process annotation of a particular property
*/
private static void processElementAnnotations(
PropertyHolder propertyHolder, Nullability nullability, XProperty property,
PropertyHolder propertyHolder, Nullability nullability,
PropertyData inferredData, HashMap<String, IdGenerator> classGenerators,
EntityBinder entityBinder, boolean isIdentifierMapper,
boolean isComponentEmbedded, boolean inSecondPass, ExtendedMappings mappings,
@ -1246,6 +1339,7 @@ public final class
"Processing annotations of {}.{}", propertyHolder.getEntityName(), inferredData.getPropertyName()
);
final XProperty property = inferredData.getProperty();
if ( property.isAnnotationPresent( Parent.class ) ) {
if ( propertyHolder.isComponent() ) {
propertyHolder.setParentProperty( property.getName() );
@ -1794,7 +1888,7 @@ public final class
final PropertyData mapsIdProperty = BinderHelper.getPropertyOverriddenByMapperOrMapsId(
isId, propertyHolder, property.getName(), mappings
);
HashMap<String, IdGenerator> localGenerators = (HashMap<String, IdGenerator>) classGenerators.clone();
Map<String, IdGenerator> localGenerators = (HashMap<String, IdGenerator>) classGenerators.clone();
final IdGenerator foreignGenerator = new IdGenerator();
foreignGenerator.setIdentifierGeneratorStrategy( "assigned" );
foreignGenerator.setName( "Hibernate-local--foreign generator" );
@ -2061,7 +2155,7 @@ public final class
inferredData, propertyHolder, mappings
);
final XClass entityXClass = inferredData.getPropertyClass();
final XClass xClassProcessed = inferredData.getPropertyClass();
List<PropertyData> classElements = new ArrayList<PropertyData>();
XClass returnedClassOrElement = inferredData.getClassOrElement();
@ -2069,11 +2163,11 @@ public final class
Map<String, PropertyData> orderedBaseClassElements = new HashMap<String, PropertyData>();
XClass baseReturnedClassOrElement;
if(baseInferredData != null) {
baseClassElements = new ArrayList<PropertyData>();
baseReturnedClassOrElement = baseInferredData.getClassOrElement();
bindTypeDefs(baseReturnedClassOrElement, mappings);
PropertyContainer propContainer = new PropertyContainer( baseReturnedClassOrElement, entityXClass );
addElementsOfClass( baseClassElements, propertyAccessor, propContainer, mappings );
baseClassElements = new ArrayList<PropertyData>();
baseReturnedClassOrElement = baseInferredData.getClassOrElement();
bindTypeDefs(baseReturnedClassOrElement, mappings);
PropertyContainer propContainer = new PropertyContainer( baseReturnedClassOrElement, xClassProcessed );
addElementsOfClass( baseClassElements, propertyAccessor, propContainer, mappings );
for (PropertyData element : baseClassElements) {
orderedBaseClassElements.put( element.getPropertyName(), element );
}
@ -2081,39 +2175,43 @@ public final class
//embeddable elements can have type defs
bindTypeDefs(returnedClassOrElement, mappings);
PropertyContainer propContainer = new PropertyContainer( returnedClassOrElement, entityXClass );
PropertyContainer propContainer = new PropertyContainer( returnedClassOrElement, xClassProcessed );
addElementsOfClass( classElements, propertyAccessor, propContainer, mappings);
//add elements of the embeddable superclass
XClass superClass = entityXClass.getSuperclass();
XClass superClass = xClassProcessed.getSuperclass();
while ( superClass != null && superClass.isAnnotationPresent( MappedSuperclass.class ) ) {
//FIXME: proper support of typevariables incl var resolved at upper levels
propContainer = new PropertyContainer( superClass, entityXClass );
propContainer = new PropertyContainer( superClass, xClassProcessed );
addElementsOfClass( classElements, propertyAccessor, propContainer, mappings );
superClass = superClass.getSuperclass();
}
if ( baseClassElements != null ) {
//useful to avoid breaking pre JPA 2 mappings
if ( !hasAnnotationsOnIdClass( entityXClass ) ) {
if ( !hasAnnotationsOnIdClass( xClassProcessed ) ) {
for ( int i = 0; i < classElements.size(); i++ ) {
final PropertyData idClassPropertyData = classElements.get( i );
final PropertyData entityPropertyData = orderedBaseClassElements.get( idClassPropertyData.getPropertyName() );
//FIXME
if ( entityPropertyData != null ) {
if ( propertyHolder.isInIdClass() ) {
if ( entityPropertyData.getProperty().isAnnotationPresent( ManyToOne.class )
|| entityPropertyData.getProperty().isAnnotationPresent( OneToOne.class ) ) {
//don't replace here as we need to use the actual original return type
//the annotation overriding will be dealt with by a mechanism similar to @MapsId
}
else {
classElements.set( i, entityPropertyData ); //this works since they are in the same order
}
if ( propertyHolder.isInIdClass() ) {
if (entityPropertyData == null) {
throw new AnnotationException (
"Property of @IdClass not found in entity "
+ baseInferredData.getPropertyClass().getName() + ": "
+ idClassPropertyData.getPropertyName()
);
}
if ( entityPropertyData.getProperty().isAnnotationPresent( ManyToOne.class )
|| entityPropertyData.getProperty().isAnnotationPresent( OneToOne.class ) ) {
//don't replace here as we need to use the actual original return type
//the annotation overriding will be dealt with by a mechanism similar to @MapsId
}
else {
classElements.set( i, entityPropertyData ); //this works since they are in the same order
}
}
else {
classElements.set( i, entityPropertyData ); //this works since they are in the same order
}
}
}
}
@ -2122,7 +2220,7 @@ public final class
subHolder, isNullable ?
Nullability.NO_CONSTRAINT :
Nullability.FORCED_NOT_NULL,
propertyAnnotatedElement.getProperty(), propertyAnnotatedElement,
propertyAnnotatedElement,
new HashMap<String, IdGenerator>(), entityBinder, isIdentifierMapper, isComponentEmbedded,
inSecondPass, mappings, inheritanceStatePerClass
);
@ -2131,7 +2229,7 @@ public final class
if(property.isAnnotationPresent(GeneratedValue.class) &&
property.isAnnotationPresent(Id.class) ) {
//clone classGenerator and override with local values
HashMap<String, IdGenerator> localGenerators = new HashMap<String, IdGenerator>();
Map<String, IdGenerator> localGenerators = new HashMap<String, IdGenerator>();
localGenerators.putAll( buildLocalGenerators( property, mappings ) );
GeneratedValue generatedValue = property.getAnnotation( GeneratedValue.class );
@ -2161,10 +2259,9 @@ public final class
return comp;
}
private static void bindId(
private static void bindIdClass(
String generatorType, String generatorName, PropertyData inferredData,
PropertyData baseInferredData, Ejb3Column[] columns, PropertyHolder propertyHolder,
Map<String, IdGenerator> localGenerators,
boolean isComposite,
AccessType propertyAccessor, EntityBinder entityBinder, boolean isEmbedded,
boolean isIdentifierMapper, ExtendedMappings mappings,
@ -2185,6 +2282,7 @@ public final class
String persistentClassName = rootClass.getClassName();
SimpleValue id;
final String propertyName = inferredData.getPropertyName();
HashMap<String, IdGenerator> localGenerators = new HashMap<String, IdGenerator>();
if ( isComposite ) {
id = fillComponent(
propertyHolder, inferredData, baseInferredData, propertyAccessor,
@ -2203,6 +2301,8 @@ public final class
setupComponentTuplizer( property, componentId );
}
else {
//TODO I think this branch is never used. Remove.
for (Ejb3Column column : columns) {
column.forceNotNull(); //this is an id
}
@ -2244,6 +2344,16 @@ public final class
}
}
private static PropertyData getUniqueIdPropertyFromBaseClass(PropertyData inferredData, PropertyData baseInferredData, AccessType propertyAccessor, ExtendedMappings mappings) {
List<PropertyData> baseClassElements = new ArrayList<PropertyData>();
XClass baseReturnedClassOrElement = baseInferredData.getClassOrElement();
PropertyContainer propContainer = new PropertyContainer( baseReturnedClassOrElement, inferredData.getPropertyClass() );
addElementsOfClass( baseClassElements, propertyAccessor, propContainer, mappings );
//Id properties are on top and there is only one
final PropertyData idPropertyOnBaseClass = baseClassElements.get( 0 );
return idPropertyOnBaseClass;
}
private static void setupComponentTuplizer(XProperty property, Component component) {
if ( property == null ) return;
if ( property.isAnnotationPresent( Tuplizers.class ) ) {

View File

@ -185,10 +185,19 @@ class ColumnsBuilder {
Ejb3Column[] result = columns;
final PropertyData overridingProperty = BinderHelper.getPropertyOverriddenByMapperOrMapsId( isId, propertyHolder, property.getName(), mappings );
if ( overridingProperty != null ) {
result = buildExplicitJoinColumns( overridingProperty.getProperty(), overridingProperty );
if (result == null) {
result = buildDefaultJoinColumnsForXToOne( overridingProperty.getProperty(), overridingProperty);
}
result = buildExcplicitOrDefaultJoinColumn( overridingProperty );
}
return result;
}
/**
* useful to override a column either by @MapsId or by @IdClass
*/
Ejb3Column[] buildExcplicitOrDefaultJoinColumn(PropertyData overridingProperty) {
Ejb3Column[] result;
result = buildExplicitJoinColumns( overridingProperty.getProperty(), overridingProperty );
if (result == null) {
result = buildDefaultJoinColumnsForXToOne( overridingProperty.getProperty(), overridingProperty);
}
return result;
}

View File

@ -0,0 +1,46 @@
package org.hibernate.test.annotations.derivedidentities.e5.a;
import org.hibernate.Session;
import org.hibernate.junit.FailureExpected;
import org.hibernate.test.annotations.TestCase;
import org.hibernate.test.util.SchemaUtil;
/**
* @author Emmanuel Bernard
*/
public class DerivedIdentityIdClassParentSameIdTypeIdClassDepTest extends TestCase {
public void testOneToOneExplicitJoinColumn() throws Exception {
assertTrue( SchemaUtil.isColumnPresent( "MedicalHistory", "FK1", getCfg() ) );
assertTrue( SchemaUtil.isColumnPresent( "MedicalHistory", "FK2", getCfg() ) );
assertTrue( ! SchemaUtil.isColumnPresent( "MedicalHistory", "firstname", getCfg() ) );
Person e = new Person();
e.firstName = "Emmanuel";
e.lastName = "Bernard";
Session s = openSession( );
s.getTransaction().begin();
s.persist( e );
MedicalHistory d = new MedicalHistory();
d.patient = e;
s.persist( d );
s.flush();
s.clear();
PersonId pId = new PersonId();
pId.firstName = e.firstName;
pId.lastName = e.lastName;
d = (MedicalHistory) s.get( MedicalHistory.class, pId);
assertEquals( pId.firstName, d.patient.firstName );
s.delete( d );
s.delete( d.patient );
s.getTransaction().rollback();
s.close();
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
MedicalHistory.class,
Person.class
};
}
}

View File

@ -0,0 +1,24 @@
package org.hibernate.test.annotations.derivedidentities.e5.a;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.OneToOne;
/**
* @author Emmanuel Bernard
*/
@Entity
@IdClass(PersonId.class)
public class MedicalHistory implements Serializable {
@Id
@JoinColumns({
@JoinColumn(name = "FK1", referencedColumnName = "firstName"),
@JoinColumn(name = "FK2", referencedColumnName = "lastName")
})
@OneToOne
Person patient;
}

View File

@ -0,0 +1,16 @@
package org.hibernate.test.annotations.derivedidentities.e5.a;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
/**
* @author Emmanuel Bernard
*/
@Entity
@IdClass(PersonId.class)
public class Person {
@Id String firstName;
@Id String lastName;
}

View File

@ -0,0 +1,11 @@
package org.hibernate.test.annotations.derivedidentities.e5.a;
import java.io.Serializable;
/**
* @author Emmanuel Bernard
*/
public class PersonId implements Serializable {
String firstName;
String lastName;
}

View File

@ -0,0 +1,47 @@
package org.hibernate.test.annotations.derivedidentities.e6.a;
import org.hibernate.Session;
import org.hibernate.junit.FailureExpected;
import org.hibernate.test.annotations.TestCase;
import org.hibernate.test.util.SchemaUtil;
/**
* @author Emmanuel Bernard
*/
public class DerivedIdentityEmbeddedIdParentSameIdTypeIdClassDepTest extends TestCase {
public void testOneToOneExplicitJoinColumn() throws Exception {
assertTrue( SchemaUtil.isColumnPresent( "MedicalHistory", "FK1", getCfg() ) );
assertTrue( SchemaUtil.isColumnPresent( "MedicalHistory", "FK2", getCfg() ) );
assertTrue( ! SchemaUtil.isColumnPresent( "MedicalHistory", "firstname", getCfg() ) );
Person e = new Person();
e.id = new PersonId();
e.id.firstName = "Emmanuel";
e.id.lastName = "Bernard";
Session s = openSession( );
s.getTransaction().begin();
s.persist( e );
MedicalHistory d = new MedicalHistory();
d.patient = e;
s.persist( d );
s.flush();
s.clear();
PersonId pId = new PersonId();
pId.firstName = e.id.firstName;
pId.lastName = e.id.lastName;
d = (MedicalHistory) s.get( MedicalHistory.class, pId );
assertEquals( pId.firstName, d.patient.id.firstName );
s.delete( d );
s.delete( d.patient );
s.getTransaction().commit();
s.close();
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
MedicalHistory.class,
Person.class
};
}
}

View File

@ -0,0 +1,26 @@
package org.hibernate.test.annotations.derivedidentities.e6.a;
import java.io.Serializable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.MapsId;
import javax.persistence.OneToOne;
/**
* @author Emmanuel Bernard
*/
@Entity
@IdClass(PersonId.class)
public class MedicalHistory implements Serializable {
@Id
@JoinColumns({
@JoinColumn(name = "FK1", referencedColumnName = "firstName"),
@JoinColumn(name = "FK2", referencedColumnName = "lastName")
})
@OneToOne
Person patient;
}

View File

@ -0,0 +1,13 @@
package org.hibernate.test.annotations.derivedidentities.e6.a;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
/**
* @author Emmanuel Bernard
*/
@Entity
public class Person {
@EmbeddedId
PersonId id;
}

View File

@ -0,0 +1,13 @@
package org.hibernate.test.annotations.derivedidentities.e6.a;
import java.io.Serializable;
import javax.persistence.Embeddable;
/**
* @author Emmanuel Bernard
*/
@Embeddable
public class PersonId implements Serializable {
String firstName;
String lastName;
}