HHH-4529 support for derived entity id as a XToOne pointing to the master entity

HHH-4840 support for Core style embedded id (after all these years :) )

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18619 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Emmanuel Bernard 2010-01-25 17:19:05 +00:00
parent 671d9db8e2
commit d00f7efb30
12 changed files with 439 additions and 102 deletions

View File

@ -625,10 +625,10 @@ public final class AnnotationBinder {
HashMap<String, IdGenerator> classGenerators = buildLocalGenerators( clazzToProcess, mappings );
// check properties
List<PropertyData> elements =
getElementsToProcess(
final ElementsToProcess elementsToProcess = getElementsToProcess(
persistentClass, clazzToProcess, inheritanceStatePerClass, entityBinder, mappings
);
final boolean subclassAndSingleTableStrategy = inheritanceState.getType() == InheritanceType.SINGLE_TABLE
&& inheritanceState.hasParents();
//process idclass if any
@ -674,7 +674,9 @@ public final class AnnotationBinder {
isComponent,
propertyAccessor, entityBinder,
true,
false, mappings, inheritanceStatePerClass
false,
mappings,
inheritanceStatePerClass
);
inferredData = new PropertyPreloadedData(
propertyAccessor, "_identifierMapper", compositeClass
@ -683,9 +685,11 @@ public final class AnnotationBinder {
propertyHolder,
inferredData,
baseInferredData,
propertyAccessor, false,
propertyAccessor,
false,
entityBinder,
true, true,
true,
true,
false, mappings, inheritanceStatePerClass
);
entityBinder.setIgnoreIdAnnotations( ignoreIdAnnotations );
@ -720,8 +724,11 @@ public final class AnnotationBinder {
idProperties.add( ( (Property) properties.next() ).getName() );
}
}
else {
entityBinder.setWrapIdsInEmbeddedComponents( elementsToProcess.getIdPropertyCount() > 1 );
}
Set<String> missingIdProperties = new HashSet<String>( idProperties );
for (PropertyData propertyAnnotatedElement : elements) {
for (PropertyData propertyAnnotatedElement : elementsToProcess.getElements() ) {
String propertyName = propertyAnnotatedElement.getPropertyName();
if ( !idProperties.contains( propertyName ) ) {
processElementAnnotations(
@ -979,7 +986,7 @@ public final class AnnotationBinder {
* Get the annotated elements, guessing the access type from @Id or @EmbeddedId presence.
* Change EntityBinder by side effect
*/
private static List<PropertyData> getElementsToProcess(
private static ElementsToProcess getElementsToProcess(
PersistentClass persistentClass, XClass clazzToProcess,
Map<XClass, InheritanceState> inheritanceStatePerClass,
EntityBinder entityBinder, ExtendedMappings mappings
@ -996,21 +1003,39 @@ public final class AnnotationBinder {
List<PropertyData> elements = new ArrayList<PropertyData>();
int deep = classesToProcess.size();
boolean hasIdentifier = false;
int idPropertyCount = 0;
for ( int index = 0; index < deep; index++ ) {
PropertyContainer properyContainer = new PropertyContainer( classesToProcess.get( index ), clazzToProcess );
boolean currentHasIdentifier = addElementsOfClass( elements, accessType, properyContainer, mappings );
hasIdentifier = hasIdentifier || currentHasIdentifier;
PropertyContainer propertyContainer = new PropertyContainer( classesToProcess.get( index ), clazzToProcess );
int currentIdPropertyCount = addElementsOfClass( elements, accessType, propertyContainer, mappings );
idPropertyCount += currentIdPropertyCount;
}
entityBinder.setPropertyAccessType( accessType );
if ( !hasIdentifier && !inheritanceState.hasParents() ) {
if ( idPropertyCount == 0 && !inheritanceState.hasParents() ) {
throw new AnnotationException( "No identifier specified for entity: " + clazzToProcess.getName() );
}
return elements;
return new ElementsToProcess( elements, idPropertyCount);
}
private static final class ElementsToProcess {
private final List<PropertyData> properties;
private final int idPropertyCount;
public List<PropertyData> getElements() {
return properties;
}
public int getIdPropertyCount() {
return idPropertyCount;
}
private ElementsToProcess(List<PropertyData> properties, int idPropertyCount) {
this.properties = properties;
this.idPropertyCount = idPropertyCount;
}
}
private static AccessType determineDefaultAccessType(XClass annotatedClass, Map<XClass, InheritanceState> inheritanceStatePerClass) {
@ -1207,13 +1232,13 @@ public final class AnnotationBinder {
* strategy is used
* @param propertyContainer Metadata about a class and its properties
* @param mappings Mapping meta data
* @return {@code true} in case an id property was found while iterating the elements of {@code annoatedClass} using
* @return the number of id properties found while iterating the elements of {@code annoatedClass} using
* the determined access strategy, {@code false} otherwise.
*/
private static boolean addElementsOfClass(
private static int addElementsOfClass(
List<PropertyData> elements, AccessType defaultAccessType, PropertyContainer propertyContainer, ExtendedMappings mappings
) {
boolean hasIdentifier = false;
int idPropertyCounter = 0;
AccessType accessType = defaultAccessType;
if ( propertyContainer.hasExplicitAccessStrategy() ) {
@ -1223,21 +1248,21 @@ public final class AnnotationBinder {
propertyContainer.assertTypesAreResolvable( accessType );
Collection<XProperty> properties = propertyContainer.getProperties( accessType );
for ( XProperty p : properties ) {
final boolean currentHasIdentifier = addProperty(
final int currentIdPropertyCounter = addProperty(
propertyContainer, p, elements, accessType.getType(), mappings
);
hasIdentifier = hasIdentifier || currentHasIdentifier;
idPropertyCounter += currentIdPropertyCounter;
}
return hasIdentifier;
return idPropertyCounter;
}
private static boolean addProperty(
private static int addProperty(
PropertyContainer propertyContainer, XProperty property, List<PropertyData> annElts,
String propertyAccessor, ExtendedMappings mappings
) {
final XClass declaringClass = propertyContainer.getDeclaringClass();
final XClass entity = propertyContainer.getEntityAtStake();
boolean hasIdentifier;
int idPropertyCounter = 0;
PropertyData propertyAnnotatedElement = new PropertyInferredData(
declaringClass, property, propertyAccessor,
mappings.getReflectionManager() );
@ -1249,17 +1274,16 @@ public final class AnnotationBinder {
final XAnnotatedElement element = propertyAnnotatedElement.getProperty();
if ( element.isAnnotationPresent( Id.class ) || element.isAnnotationPresent( EmbeddedId.class ) ) {
annElts.add( 0, propertyAnnotatedElement );
hasIdentifier = true;
idPropertyCounter++;
}
else {
annElts.add( propertyAnnotatedElement );
hasIdentifier = false;
}
if ( element.isAnnotationPresent( MapsId.class ) ) {
mappings.addPropertyAnnotatedWithMapsId( entity, propertyAnnotatedElement );
}
return hasIdentifier;
return idPropertyCounter;
}
/*
@ -1361,9 +1385,28 @@ public final class AnnotationBinder {
final XClass returnedClass = inferredData.getClassOrElement();
//prepare PropertyBinder
PropertyBinder propertyBinder = new PropertyBinder();
propertyBinder.setName( inferredData.getPropertyName() );
propertyBinder.setReturnedClassName( inferredData.getTypeName() );
propertyBinder.setAccessType( inferredData.getDefaultAccess() );
propertyBinder.setHolder( propertyHolder );
propertyBinder.setProperty( property );
propertyBinder.setReturnedClass( inferredData.getPropertyClass() );
propertyBinder.setMappings( mappings );
if ( isIdentifierMapper ) {
propertyBinder.setInsertable( false );
propertyBinder.setUpdatable( false );
}
propertyBinder.setDeclaringClass( inferredData.getDeclaringClass() );
propertyBinder.setEntityBinder( entityBinder );
propertyBinder.setInheritanceStatePerClass(inheritanceStatePerClass);
boolean isId = !entityBinder.isIgnoreIdAnnotations() &&
( property.isAnnotationPresent( Id.class )
|| property.isAnnotationPresent( EmbeddedId.class ) );
propertyBinder.setId( isId );
if ( property.isAnnotationPresent( Version.class ) ) {
if ( isIdentifierMapper ) {
throw new AnnotationException(
@ -1384,19 +1427,14 @@ public final class AnnotationBinder {
}
log.trace( "{} is a version property", inferredData.getPropertyName() );
RootClass rootClass = (RootClass) propertyHolder.getPersistentClass();
PropertyBinder propBinder = new PropertyBinder();
propBinder.setName( inferredData.getPropertyName() );
propBinder.setReturnedClassName( inferredData.getTypeName() );
propBinder.setLazy( false );
propBinder.setAccessType( inferredData.getDefaultAccess() );
propBinder.setColumns( columns );
propBinder.setHolder( propertyHolder ); //PropertyHolderBuilder.buildPropertyHolder(rootClass)
propBinder.setProperty( property );
propBinder.setReturnedClass( inferredData.getPropertyClass() );
propBinder.setMappings( mappings );
propBinder.setDeclaringClass( inferredData.getDeclaringClass() );
Property prop = propBinder.makePropertyValueAndBind();
propBinder.getSimpleValueBinder().setVersion(true);
// PropertyBinder propBinder = new PropertyBinder();
// propBinder.setName( inferredData.getPropertyName() );
// propBinder.setReturnedClassName( inferredData.getTypeName() );
// propBinder.setLazy( false );
// propBinder.setAccessType( inferredData.getDefaultAccess() );
propertyBinder.setColumns( columns );
Property prop = propertyBinder.makePropertyValueAndBind();
propertyBinder.getSimpleValueBinder().setVersion(true);
rootClass.setVersion( prop );
//If version is on a mapped superclass, update the mapping
@ -1451,7 +1489,8 @@ public final class AnnotationBinder {
ignoreNotFound, onDeleteCascade,
ToOneBinder.getTargetEntity( inferredData, mappings ),
propertyHolder,
inferredData, false, isIdentifierMapper, inSecondPass, mappings
inferredData, false, isIdentifierMapper,
inSecondPass, propertyBinder, mappings
);
}
else if ( property.isAnnotationPresent( OneToOne.class ) ) {
@ -1489,7 +1528,13 @@ public final class AnnotationBinder {
ignoreNotFound, onDeleteCascade,
ToOneBinder.getTargetEntity( inferredData, mappings ),
propertyHolder,
inferredData, ann.mappedBy(), trueOneToOne, isIdentifierMapper, inSecondPass, mappings
inferredData,
ann.mappedBy(),
trueOneToOne,
isIdentifierMapper,
inSecondPass,
propertyBinder,
mappings
);
}
else if ( property.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) {
@ -1793,7 +1838,7 @@ public final class AnnotationBinder {
isComponent = property.isAnnotationPresent( Embedded.class )
|| property.isAnnotationPresent( EmbeddedId.class )
|| returnedClass.isAnnotationPresent( Embeddable.class );
PropertyBinder propertyBinder;
if ( isComponent ) {
AccessType propertyAccessor = entityBinder.getPropertyAccessor( property );
propertyBinder = bindComponent(
@ -1835,23 +1880,12 @@ public final class AnnotationBinder {
mappings );
}
propertyBinder = new PropertyBinder();
propertyBinder.setName( inferredData.getPropertyName() );
propertyBinder.setReturnedClassName( inferredData.getTypeName() );
propertyBinder.setLazy( lazy );
propertyBinder.setAccessType( inferredData.getDefaultAccess() );
propertyBinder.setColumns( columns );
propertyBinder.setHolder( propertyHolder );
propertyBinder.setProperty( property );
propertyBinder.setReturnedClass( inferredData.getPropertyClass() );
propertyBinder.setMappings( mappings );
if ( isIdentifierMapper ) {
propertyBinder.setInsertable( false );
propertyBinder.setUpdatable( false );
}
propertyBinder.setDeclaringClass( inferredData.getDeclaringClass() );
propertyBinder.setId(isId);
propertyBinder.setInheritanceStatePerClass(inheritanceStatePerClass);
// if ( isIdentifierMapper ) {
// propertyBinder.setInsertable( false );
// propertyBinder.setUpdatable( false );
// }
propertyBinder.makePropertyValueAndBind();
}
if (isId) {
@ -2105,6 +2139,7 @@ public final class AnnotationBinder {
binder.setEmbedded( isComponentEmbedded );
binder.setHolder( propertyHolder );
binder.setId( isId );
binder.setEntityBinder( entityBinder );
binder.setInheritanceStatePerClass( inheritanceStatePerClass );
binder.setMappings( mappings );
binder.makePropertyAndBind();
@ -2136,17 +2171,7 @@ public final class AnnotationBinder {
* Because it's a value type, there is no bidirectional association, hence second pass
* ordering does not matter
*/
Component comp = new Component( propertyHolder.getPersistentClass() );
comp.setEmbedded( isComponentEmbedded );
//yuk
comp.setTable( propertyHolder.getTable() );
if ( !isIdentifierMapper ) {
comp.setComponentClassName( inferredData.getClassOrElementName() );
}
else {
comp.setComponentClassName( comp.getOwner().getClassName() );
}
comp.setNodeName( inferredData.getPropertyName() );
Component comp = createComponent( propertyHolder, inferredData, isComponentEmbedded, isIdentifierMapper );
String subpath = BinderHelper.getPath( propertyHolder, inferredData );
log.trace( "Binding component with path: {}", subpath );
PropertyHolder subHolder = PropertyHolderBuilder.buildPropertyHolder(
@ -2202,6 +2227,22 @@ public final class AnnotationBinder {
return comp;
}
public static Component createComponent(PropertyHolder propertyHolder, PropertyData inferredData, boolean isComponentEmbedded, boolean isIdentifierMapper) {
Component comp = new Component( propertyHolder.getPersistentClass() );
comp.setEmbedded( isComponentEmbedded );
//yuk
comp.setTable( propertyHolder.getTable() );
//FIXME shouldn't identifier mapper use getClassOrElementName? Need to be checked.
if ( isIdentifierMapper || ( isComponentEmbedded && inferredData.getPropertyName() == null ) ) {
comp.setComponentClassName( comp.getOwner().getClassName() );
}
else {
comp.setComponentClassName( inferredData.getClassOrElementName() );
}
comp.setNodeName( inferredData.getPropertyName() );
return comp;
}
private static void bindId(
String generatorType, String generatorName,
PropertyData inferredData, Ejb3Column[] columns, PropertyHolder propertyHolder,
@ -2345,7 +2386,9 @@ public final class AnnotationBinder {
String cascadeStrategy, Ejb3JoinColumn[] columns, boolean optional,
boolean ignoreNotFound, boolean cascadeOnDelete,
XClass targetEntity, PropertyHolder propertyHolder,
PropertyData inferredData, boolean unique, boolean isIdentifierMapper, boolean inSecondPass,
PropertyData inferredData, boolean unique,
boolean isIdentifierMapper, boolean inSecondPass,
PropertyBinder propertyBinder,
ExtendedMappings mappings
) {
//All FK columns should be in the same table
@ -2399,24 +2442,26 @@ public final class AnnotationBinder {
);
}
Ejb3Column.checkPropertyConsistency( columns, propertyHolder.getEntityName() + propertyName );
PropertyBinder binder = new PropertyBinder();
binder.setName( propertyName );
binder.setValue( value );
//PropertyBinder binder = new PropertyBinder();
propertyBinder.setName( propertyName );
propertyBinder.setValue( value );
//binder.setCascade(cascadeStrategy);
if ( isIdentifierMapper ) {
binder.setInsertable( false );
binder.setUpdatable( false );
propertyBinder.setInsertable( false );
propertyBinder.setUpdatable( false );
}
else {
binder.setInsertable( columns[0].isInsertable() );
binder.setUpdatable( columns[0].isUpdatable() );
propertyBinder.setInsertable( columns[0].isInsertable() );
propertyBinder.setUpdatable( columns[0].isUpdatable() );
}
binder.setAccessType( inferredData.getDefaultAccess() );
binder.setCascade( cascadeStrategy );
binder.setProperty( property );
Property prop = binder.makeProperty();
propertyBinder.setColumns( columns );
propertyBinder.setAccessType( inferredData.getDefaultAccess() );
propertyBinder.setCascade( cascadeStrategy );
propertyBinder.setProperty( property );
propertyBinder.setXToMany( true );
Property prop = propertyBinder.makePropertyAndBind();
//composite FK columns are in the same table so its OK
propertyHolder.addProperty( prop, columns, inferredData.getDeclaringClass() );
//propertyHolder.addProperty( prop, columns, inferredData.getDeclaringClass() );
}
protected static void defineFetchingStrategy(ToOne toOne, XProperty property) {
@ -2476,7 +2521,10 @@ public final class AnnotationBinder {
PropertyHolder propertyHolder,
PropertyData inferredData, String mappedBy,
boolean trueOneToOne,
boolean isIdentifierMapper, boolean inSecondPass, ExtendedMappings mappings
boolean isIdentifierMapper,
boolean inSecondPass,
PropertyBinder propertyBinder,
ExtendedMappings mappings
) {
//column.getTable() => persistentClass.getTable()
final String propertyName = inferredData.getPropertyName();
@ -2535,7 +2583,8 @@ public final class AnnotationBinder {
bindManyToOne(
cascadeStrategy, joinColumns, optional, ignoreNotFound, cascadeOnDelete,
targetEntity,
propertyHolder, inferredData, true, isIdentifierMapper, inSecondPass, mappings
propertyHolder, inferredData, true, isIdentifierMapper, inSecondPass,
propertyBinder, mappings
);
}
}

View File

@ -119,6 +119,11 @@ public class EntityBinder {
private boolean ignoreIdAnnotations;
private boolean cacheLazyProperty;
private AccessType propertyAccessType = AccessType.DEFAULT;
private boolean wrapIdsInEmbeddedComponents;
public boolean wrapIdsInEmbeddedComponents() {
return wrapIdsInEmbeddedComponents;
}
/**
* Use as a fake one for Collection of elements
@ -413,6 +418,11 @@ public class EntityBinder {
}
}
public void setWrapIdsInEmbeddedComponents(boolean wrapIdsInEmbeddedComponents) {
this.wrapIdsInEmbeddedComponents = wrapIdsInEmbeddedComponents;
}
private static class EntityTableObjectNameSource implements ObjectNameSource {
private final String explicitName;
private final String logicalName;

View File

@ -40,11 +40,14 @@ import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.cfg.AccessType;
import org.hibernate.cfg.AnnotationBinder;
import org.hibernate.cfg.BinderHelper;
import org.hibernate.cfg.Ejb3Column;
import org.hibernate.cfg.ExtendedMappings;
import org.hibernate.cfg.InheritanceState;
import org.hibernate.cfg.PropertyHolder;
import org.hibernate.cfg.PropertyPreloadedData;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.PropertyGeneration;
@ -73,11 +76,17 @@ public class PropertyBinder {
private XClass declaringClass;
private boolean declaringClassSet;
private boolean embedded;
private EntityBinder entityBinder;
private boolean isXToMany;
public void setEmbedded(boolean embedded) {
this.embedded = embedded;
}
public void setEntityBinder(EntityBinder entityBinder) {
this.entityBinder = entityBinder;
}
/*
* property can be null
* prefer propertyName to property.getName() since some are overloaded
@ -185,11 +194,28 @@ public class PropertyBinder {
return bind( makePropertyAndValue() );
}
public void setXToMany(boolean xToMany) {
this.isXToMany = xToMany;
}
private Property bind(Property prop) {
if (isId) {
final RootClass rootClass = ( RootClass ) holder.getPersistentClass();
//if an xToMany, it as to be wrapped today.
//FIXME this pose a problem as the PK is the class instead of the associated class which is not really compliant with the spec
if ( isXToMany || entityBinder.wrapIdsInEmbeddedComponents() ) {
Component identifier = (Component) rootClass.getIdentifier();
if (identifier == null) {
identifier = AnnotationBinder.createComponent( holder, new PropertyPreloadedData(null, null, null), true, false );
rootClass.setIdentifier( identifier );
identifier.setNullValue( "undefined" );
rootClass.setEmbeddedIdentifier( true );
}
//FIXME is it good enough?
identifier.addProperty( prop );
}
else {
rootClass.setIdentifier( ( KeyValue ) getValue() );
if (embedded) {
rootClass.setEmbeddedIdentifier( true );
}
@ -209,6 +235,7 @@ public class PropertyBinder {
}
}
}
}
else {
holder.addProperty( prop, columns, declaringClass );
}

View File

@ -1,4 +1,4 @@
// $Id:$
// $Id$
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
@ -24,6 +24,8 @@
*/
package org.hibernate.cfg.annotations;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -34,7 +36,7 @@ public class Version {
private static Logger log = LoggerFactory.getLogger( Version.class );
public static String getVersionString() {
return "[WORKING]";
return "[WORKING]-1";
}
static {

View File

@ -0,0 +1,25 @@
package org.hibernate.test.annotations.derivedidentities.e1.c;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
/**
* @author Emmanuel Bernard
*/
@Entity
public class Dependent implements Serializable {
@Id
String name;
@Id
//@JoinColumn(name = "FK")
// id attribute mapped by join column default
@ManyToOne
Employee emp;
}

View File

@ -0,0 +1,46 @@
package org.hibernate.test.annotations.derivedidentities.e1.c;
import org.hibernate.Session;
import org.hibernate.test.annotations.TestCase;
import org.hibernate.test.util.SchemaUtil;
/**
* @author Emmanuel Bernard
*/
public class DerivedIdentitySimpleParentEmbeddedDepTest extends TestCase {
public void testManyToOne() throws Exception {
assertTrue( SchemaUtil.isColumnPresent( "Dependent", "emp_empId", getCfg() ) );
assertTrue( ! SchemaUtil.isColumnPresent( "Dependent", "empPK", getCfg() ) );
Employee e = new Employee();
e.empId = 1;
e.empName = "Emmanuel";
Session s = openSession( );
s.getTransaction().begin();
s.persist( e );
Dependent d = new Dependent();
d.emp = e;
d.name = "Doggy";
s.persist( d );
s.flush();
s.clear();
d = getDerivedClassById( e, s, Dependent.class, d.name );
assertEquals( e.empId, d.emp.empId );
s.getTransaction().rollback();
s.close();
}
private <T> T getDerivedClassById(Employee e, Session s, Class<T> clazz, String name) {
return ( T )
s.createQuery( "from " + clazz.getName() + " d where d.name = :name and d.emp.empId = :empId")
.setParameter( "empId", e.empId ).setParameter( "name", name ).uniqueResult();
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Dependent.class,
Employee.class
};
}
}

View File

@ -0,0 +1,14 @@
package org.hibernate.test.annotations.derivedidentities.e1.c;
import javax.persistence.Entity;
import javax.persistence.Id;
/**
* @author Emmanuel Bernard
*/
@Entity
public class Employee {
@Id
long empId;
String empName;
}

View File

@ -0,0 +1,78 @@
package org.hibernate.test.annotations.derivedidentities.e4.a;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.test.annotations.TestCase;
import org.hibernate.test.util.SchemaUtil;
/**
* @author Emmanuel Bernard
*/
public class DerivedIdentitySimpleParentSimpleDepTest extends TestCase {
public void testOneToOneExplicitJoinColumn() throws Exception {
assertTrue( SchemaUtil.isColumnPresent( "MedicalHistory", "FK", getCfg() ) );
assertTrue( ! SchemaUtil.isColumnPresent( "MedicalHistory", "id", getCfg() ) );
Person e = new Person();
e.ssn = "aaa";
Session s = openSession( );
s.getTransaction().begin();
s.persist( e );
MedicalHistory d = new MedicalHistory();
d.patient = e;
s.persist( d );
s.flush();
s.clear();
final Class<MedicalHistory> clazz = MedicalHistory.class;
d = getDerivedClassById( e, s, clazz );
assertEquals( e.ssn, d.patient.ssn );
d.lastupdate = new Date();
s.flush();
s.clear();
d = getDerivedClassById( e, s, clazz );
assertNotNull( d.lastupdate );
s.getTransaction().rollback();
s.close();
}
private <T> T getDerivedClassById(Person e, Session s, Class<T> clazz) {
return ( T )
s.createQuery( "from " + clazz.getName() + " mh where mh.patient.ssn = :ssn")
.setParameter( "ssn", e.ssn ).uniqueResult();
}
public void testManyToOneExplicitJoinColumn() throws Exception {
assertTrue( SchemaUtil.isColumnPresent( "FinancialHistory", "patient_ssn", getCfg() ) );
assertTrue( ! SchemaUtil.isColumnPresent( "FinancialHistory", "id", getCfg() ) );
Person e = new Person();
e.ssn = "aaa";
Session s = openSession( );
s.getTransaction().begin();
s.persist( e );
FinancialHistory d = new FinancialHistory();
d.patient = e;
s.persist( d );
s.flush();
s.clear();
d = getDerivedClassById(e, s, FinancialHistory.class);
assertEquals( e.ssn, d.patient.ssn );
d.lastupdate = new Date();
s.flush();
s.clear();
d = getDerivedClassById(e, s, FinancialHistory.class);
assertNotNull( d.lastupdate );
s.getTransaction().rollback();
s.close();
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
MedicalHistory.class,
Simple.class,
Person.class,
FinancialHistory.class
};
}
}

View File

@ -0,0 +1,28 @@
package org.hibernate.test.annotations.derivedidentities.e4.a;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MapsId;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
/**
* @author Emmanuel Bernard
*/
@Entity
public class FinancialHistory implements Serializable {
@Temporal(TemporalType.DATE)
Date lastupdate;
@Id
//@JoinColumn(name = "FK")
@ManyToOne
Person patient;
}

View File

@ -0,0 +1,27 @@
package org.hibernate.test.annotations.derivedidentities.e4.a;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapsId;
import javax.persistence.OneToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
/**
* @author Emmanuel Bernard
*/
@Entity
public class MedicalHistory implements Serializable {
@Temporal(TemporalType.DATE)
Date lastupdate;
@Id
@JoinColumn(name = "FK")
@OneToOne
Person patient;
}

View File

@ -0,0 +1,14 @@
package org.hibernate.test.annotations.derivedidentities.e4.a;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
/**
* @author Emmanuel Bernard
*/
@Entity
public class Person {
@Id
String ssn;
}

View File

@ -0,0 +1,17 @@
package org.hibernate.test.annotations.derivedidentities.e4.a;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
/**
* @author Emmanuel Bernard
*/
@Entity
public class Simple
implements Serializable {
@Id
String ssn;
@Id
String name;
}