HHH-4529 Add support for parent's id being an IdClass or an EmbeddedId (last case not tested yet). (example e2.b)

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18625 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Emmanuel Bernard 2010-01-26 12:57:01 +00:00
parent c57ab61dcf
commit 5ee9ba361c
11 changed files with 783 additions and 369 deletions

View File

@ -52,7 +52,6 @@ import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
@ -105,7 +104,6 @@ import org.hibernate.annotations.ForeignKey;
import org.hibernate.annotations.Formula;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Index;
import org.hibernate.annotations.JoinColumnsOrFormulas;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.annotations.ManyToAny;
@ -167,7 +165,7 @@ import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
import org.hibernate.persister.entity.SingleTableEntityPersister;
import org.hibernate.persister.entity.UnionSubclassEntityPersister;
import org.hibernate.type.TypeFactory;
import org.hibernate.util.StringHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -182,7 +180,8 @@ import org.slf4j.LoggerFactory;
* @author Hardy Ferentschik
*/
@SuppressWarnings("unchecked")
public final class AnnotationBinder {
public final class
AnnotationBinder {
/*
* Some design description
@ -625,30 +624,16 @@ public final class AnnotationBinder {
HashMap<String, IdGenerator> classGenerators = buildLocalGenerators( clazzToProcess, mappings );
// check properties
final ElementsToProcess elementsToProcess = getElementsToProcess(
persistentClass, clazzToProcess, inheritanceStatePerClass, entityBinder, mappings
);
final InheritanceState.ElementsToProcess elementsToProcess = inheritanceState.getElementsToProcess();
inheritanceState.postProcess( persistentClass, entityBinder );
final boolean subclassAndSingleTableStrategy = inheritanceState.getType() == InheritanceType.SINGLE_TABLE
&& inheritanceState.hasParents();
//process idclass if any
Set<String> idProperties = new HashSet<String>();
IdClass idClass = null;
XClass current = null;
if ( !inheritanceState.hasParents() ) {
//look for idClass
InheritanceState state = inheritanceState;
do {
current = state.getClazz();
if ( current.isAnnotationPresent( IdClass.class ) ) {
idClass = current.getAnnotation( IdClass.class );
break;
}
state = InheritanceState.getSuperclassInheritanceState( current, inheritanceStatePerClass );
}
while ( state != null );
}
if ( idClass != null ) {
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 );
@ -658,7 +643,7 @@ public final class AnnotationBinder {
entityBinder.getPropertyAccessType(), "id", compositeClass
);
PropertyData baseInferredData = new PropertyPreloadedData(
entityBinder.getPropertyAccessType(), "id", current
entityBinder.getPropertyAccessType(), "id", classWithIdClass
);
HashMap<String, IdGenerator> localGenerators = new HashMap<String, IdGenerator>();
boolean ignoreIdAnnotations = entityBinder.isIgnoreIdAnnotations();
@ -982,133 +967,126 @@ public final class AnnotationBinder {
return true;
}
/*
* Get the annotated elements, guessing the access type from @Id or @EmbeddedId presence.
* Change EntityBinder by side effect
*/
private static ElementsToProcess getElementsToProcess(
PersistentClass persistentClass, XClass clazzToProcess,
Map<XClass, InheritanceState> inheritanceStatePerClass,
EntityBinder entityBinder, ExtendedMappings mappings
) {
InheritanceState inheritanceState = inheritanceStatePerClass.get( clazzToProcess );
assert !inheritanceState.isEmbeddableSuperclass();
// /*
// * Get the annotated elements, guessing the access type from @Id or @EmbeddedId presence.
// * Change EntityBinder by side effect
// */
// private static InheritanceState.ElementsToProcess getElementsToProcess(
// PersistentClass persistentClass, XClass clazzToProcess,
// Map<XClass, InheritanceState> inheritanceStatePerClass,
// EntityBinder entityBinder, ExtendedMappings mappings
// ) {
// InheritanceState inheritanceState = inheritanceStatePerClass.get( clazzToProcess );
// assert !inheritanceState.isEmbeddableSuperclass();
//
//
// List<XClass> classesToProcess = getMappedSuperclassesTillNextEntityOrdered(
// persistentClass, clazzToProcess, inheritanceStatePerClass, mappings
// );
//
// AccessType accessType = determineDefaultAccessType( clazzToProcess, inheritanceStatePerClass );
//
// List<PropertyData> elements = new ArrayList<PropertyData>();
// int deep = classesToProcess.size();
// int idPropertyCount = 0;
//
// for ( int index = 0; index < deep; index++ ) {
// PropertyContainer propertyContainer = new PropertyContainer( classesToProcess.get( index ), clazzToProcess );
// int currentIdPropertyCount = addElementsOfClass( elements, accessType, propertyContainer, mappings );
// idPropertyCount += currentIdPropertyCount;
// }
//
// entityBinder.setPropertyAccessType( accessType );
//
// if ( idPropertyCount == 0 && !inheritanceState.hasParents() ) {
// throw new AnnotationException( "No identifier specified for entity: " + clazzToProcess.getName() );
// }
//
// return new InheritanceState.ElementsToProcess( elements, idPropertyCount);
// }
List<XClass> classesToProcess = getMappedSuperclassesTillNextEntityOrdered(
persistentClass, clazzToProcess, inheritanceStatePerClass, mappings
);
AccessType accessType = determineDefaultAccessType( clazzToProcess, inheritanceStatePerClass );
List<PropertyData> elements = new ArrayList<PropertyData>();
int deep = classesToProcess.size();
int idPropertyCount = 0;
for ( int index = 0; index < deep; index++ ) {
PropertyContainer propertyContainer = new PropertyContainer( classesToProcess.get( index ), clazzToProcess );
int currentIdPropertyCount = addElementsOfClass( elements, accessType, propertyContainer, mappings );
idPropertyCount += currentIdPropertyCount;
}
entityBinder.setPropertyAccessType( accessType );
if ( idPropertyCount == 0 && !inheritanceState.hasParents() ) {
throw new AnnotationException( "No identifier specified for entity: " + clazzToProcess.getName() );
}
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) {
XClass xclass = annotatedClass;
while ( xclass != null && !Object.class.getName().equals( xclass.getName() ) ) {
if ( xclass.isAnnotationPresent( Entity.class ) || xclass.isAnnotationPresent( MappedSuperclass.class ) ) {
for ( XProperty prop : xclass.getDeclaredProperties( AccessType.PROPERTY.getType() ) ) {
if ( prop.isAnnotationPresent( Id.class ) || prop.isAnnotationPresent( EmbeddedId.class ) ) {
return AccessType.PROPERTY;
}
}
for ( XProperty prop : xclass.getDeclaredProperties( AccessType.FIELD.getType() ) ) {
if ( prop.isAnnotationPresent( Id.class ) || prop.isAnnotationPresent( EmbeddedId.class ) ) {
return AccessType.FIELD;
}
}
}
xclass = xclass.getSuperclass();
}
throw new AnnotationException( "No identifier specified for entity: " + annotatedClass.getName() );
}
private static List<XClass> getMappedSuperclassesTillNextEntityOrdered(
PersistentClass persistentClass, XClass annotatedClass,
Map<XClass, InheritanceState> inheritanceStatePerClass,
ExtendedMappings mappings
) {
//ordered to allow proper messages on properties subclassing
List<XClass> classesToProcess = new ArrayList<XClass>();
XClass currentClassInHierarchy = annotatedClass;
InheritanceState superclassState;
final ReflectionManager reflectionManager = mappings.getReflectionManager();
do {
classesToProcess.add( 0, currentClassInHierarchy );
XClass superClass = currentClassInHierarchy;
do {
superClass = superClass.getSuperclass();
superclassState = inheritanceStatePerClass.get( superClass );
}
while ( superClass != null && !reflectionManager
.equals( superClass, Object.class ) && superclassState == null );
currentClassInHierarchy = superClass;
}
while ( superclassState != null && superclassState.isEmbeddableSuperclass() );
//add @MappedSuperclass in the metadata
// classes from 0 to n-1 are @MappedSuperclass and should be linked
org.hibernate.mapping.MappedSuperclass mappedSuperclass = null;
final InheritanceState superEntityState =
InheritanceState.getInheritanceStateOfSuperEntity( annotatedClass, inheritanceStatePerClass );
PersistentClass superEntity =
superEntityState != null ?
mappings.getClass( superEntityState.getClazz().getName() ) :
null;
final int lastMappedSuperclass = classesToProcess.size() - 1;
for ( int index = 0 ; index < lastMappedSuperclass ; index++ ) {
org.hibernate.mapping.MappedSuperclass parentSuperclass = mappedSuperclass;
final Class<?> type = mappings.getReflectionManager().toClass( classesToProcess.get( index ) );
//add MAppedSuperclass if not already there
mappedSuperclass = mappings.getMappedSuperclass( type );
if (mappedSuperclass == null) {
mappedSuperclass = new org.hibernate.mapping.MappedSuperclass(parentSuperclass, superEntity );
mappedSuperclass.setMappedClass( type );
mappings.addMappedSuperclass( type, mappedSuperclass );
}
}
if (mappedSuperclass != null) {
persistentClass.setSuperMappedSuperclass(mappedSuperclass);
}
return classesToProcess;
}
// private static AccessType determineDefaultAccessType(XClass annotatedClass, Map<XClass, InheritanceState> inheritanceStatePerClass) {
// XClass xclass = annotatedClass;
// while ( xclass != null && !Object.class.getName().equals( xclass.getName() ) ) {
// if ( xclass.isAnnotationPresent( Entity.class ) || xclass.isAnnotationPresent( MappedSuperclass.class ) ) {
// for ( XProperty prop : xclass.getDeclaredProperties( AccessType.PROPERTY.getType() ) ) {
// if ( prop.isAnnotationPresent( Id.class ) || prop.isAnnotationPresent( EmbeddedId.class ) ) {
// return AccessType.PROPERTY;
// }
// }
// for ( XProperty prop : xclass.getDeclaredProperties( AccessType.FIELD.getType() ) ) {
// if ( prop.isAnnotationPresent( Id.class ) || prop.isAnnotationPresent( EmbeddedId.class ) ) {
// return AccessType.FIELD;
// }
// }
// }
// xclass = xclass.getSuperclass();
// }
// throw new AnnotationException( "No identifier specified for entity: " + annotatedClass.getName() );
// }
//
// private static List<XClass> getMappedSuperclassesTillNextEntityOrdered(
// PersistentClass persistentClass, XClass annotatedClass,
// Map<XClass, InheritanceState> inheritanceStatePerClass,
// ExtendedMappings mappings
// ) {
//
// //ordered to allow proper messages on properties subclassing
// List<XClass> classesToProcess = new ArrayList<XClass>();
// XClass currentClassInHierarchy = annotatedClass;
// InheritanceState superclassState;
// final ReflectionManager reflectionManager = mappings.getReflectionManager();
// do {
// classesToProcess.add( 0, currentClassInHierarchy );
// XClass superClass = currentClassInHierarchy;
// do {
// superClass = superClass.getSuperclass();
// superclassState = inheritanceStatePerClass.get( superClass );
// }
// while ( superClass != null && !reflectionManager
// .equals( superClass, Object.class ) && superclassState == null );
//
// currentClassInHierarchy = superClass;
// }
// while ( superclassState != null && superclassState.isEmbeddableSuperclass() );
// addMappedSuperClassInMetadata(
// persistentClass, annotatedClass, inheritanceStatePerClass, mappings, classesToProcess
// );
//
//
// return classesToProcess;
// }
//
// private static void addMappedSuperClassInMetadata(PersistentClass persistentClass,
// XClass annotatedClass,
// Map<XClass, InheritanceState> inheritanceStatePerClass,
// ExtendedMappings mappings,
// List<XClass> classesToProcess) {
// //add @MappedSuperclass in the metadata
// // classes from 0 to n-1 are @MappedSuperclass and should be linked
// org.hibernate.mapping.MappedSuperclass mappedSuperclass = null;
// final InheritanceState superEntityState =
// InheritanceState.getInheritanceStateOfSuperEntity( annotatedClass, inheritanceStatePerClass );
// PersistentClass superEntity =
// superEntityState != null ?
// mappings.getClass( superEntityState.getClazz().getName() ) :
// null;
// final int lastMappedSuperclass = classesToProcess.size() - 1;
// for ( int index = 0 ; index < lastMappedSuperclass ; index++ ) {
// org.hibernate.mapping.MappedSuperclass parentSuperclass = mappedSuperclass;
// final Class<?> type = mappings.getReflectionManager().toClass( classesToProcess.get( index ) );
// //add MAppedSuperclass if not already there
// mappedSuperclass = mappings.getMappedSuperclass( type );
// if (mappedSuperclass == null) {
// mappedSuperclass = new org.hibernate.mapping.MappedSuperclass(parentSuperclass, superEntity );
// mappedSuperclass.setMappedClass( type );
// mappings.addMappedSuperclass( type, mappedSuperclass );
// }
// }
// if (mappedSuperclass != null) {
// persistentClass.setSuperMappedSuperclass(mappedSuperclass);
// }
// }
/*
* Process the filters defined on the given class, as well as all filters defined
@ -1235,7 +1213,7 @@ public final class AnnotationBinder {
* @return the number of id properties found while iterating the elements of {@code annoatedClass} using
* the determined access strategy, {@code false} otherwise.
*/
private static int addElementsOfClass(
static int addElementsOfClass(
List<PropertyData> elements, AccessType defaultAccessType, PropertyContainer propertyContainer, ExtendedMappings mappings
) {
int idPropertyCounter = 0;
@ -1295,14 +1273,12 @@ public final class AnnotationBinder {
EntityBinder entityBinder, boolean isIdentifierMapper,
boolean isComponentEmbedded, boolean inSecondPass, ExtendedMappings mappings,
Map<XClass, InheritanceState> inheritanceStatePerClass
)
throws MappingException {
) throws MappingException {
/**
* inSecondPass can only be used to apply right away the second pass of a composite-element
* Because it's a value type, there is no bidirectional association, hence second pass
* ordering does not matter
*/
Ejb3Column[] columns = null;
log.trace(
"Processing annotations of {}.{}", propertyHolder.getEntityName(), inferredData.getPropertyName()
@ -1318,70 +1294,14 @@ public final class AnnotationBinder {
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
return;
}
Ejb3JoinColumn[] joinColumns = buildExplicitJoinColumns(
propertyHolder, property, inferredData, entityBinder, mappings
);
if ( property.isAnnotationPresent( Column.class ) || property.isAnnotationPresent( Formula.class ) ) {
Column ann = property.getAnnotation( Column.class );
Formula formulaAnn = property.getAnnotation( Formula.class );
columns = Ejb3Column.buildColumnFromAnnotation(
new Column[] { ann }, formulaAnn, nullability, propertyHolder, inferredData,
entityBinder.getSecondaryTables(), mappings
);
}
else if ( property.isAnnotationPresent( Columns.class ) ) {
Columns anns = property.getAnnotation( Columns.class );
columns = Ejb3Column.buildColumnFromAnnotation(
anns.columns(), null, nullability, propertyHolder, inferredData, entityBinder.getSecondaryTables(),
mappings
);
return ;
}
//set default values if needed
if ( joinColumns == null &&
( property.isAnnotationPresent( ManyToOne.class )
|| property.isAnnotationPresent( OneToOne.class ) )
) {
joinColumns = buildDefaultJoinColumnsForXToOne(
propertyHolder, property, inferredData, entityBinder, mappings
);
}
else if ( joinColumns == null &&
( property.isAnnotationPresent( OneToMany.class )
|| property.isAnnotationPresent( CollectionOfElements.class ) //legacy Hibernate
|| property.isAnnotationPresent( ElementCollection.class )
) ) {
OneToMany oneToMany = property.getAnnotation( OneToMany.class );
String mappedBy = oneToMany != null ?
oneToMany.mappedBy() :
"";
joinColumns = Ejb3JoinColumn.buildJoinColumns(
(JoinColumn[]) null,
mappedBy, entityBinder.getSecondaryTables(),
propertyHolder, inferredData.getPropertyName(), mappings
);
}
else if ( joinColumns == null && property.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) {
throw new AnnotationException( "@Any requires an explicit @JoinColumn(s): "
+ BinderHelper.getPath( propertyHolder, inferredData ) );
}
if ( columns == null && !property.isAnnotationPresent( ManyToMany.class ) ) {
//useful for collection of embedded elements
columns = Ejb3Column.buildColumnFromAnnotation(
null, null, nullability, propertyHolder, inferredData, entityBinder.getSecondaryTables(), mappings
);
}
if ( nullability == Nullability.FORCED_NOT_NULL ) {
//force columns to not null
for (Ejb3Column col : columns) {
col.forceNotNull();
}
}
ColumnsBuilder columnsBuilder = new ColumnsBuilder(
propertyHolder, nullability, property, inferredData, entityBinder, mappings
).extractMetadata();
Ejb3Column[] columns = columnsBuilder.getColumns();
Ejb3JoinColumn[] joinColumns = columnsBuilder.getJoinColumns();
final XClass returnedClass = inferredData.getClassOrElement();
@ -1839,7 +1759,22 @@ public final class AnnotationBinder {
|| property.isAnnotationPresent( EmbeddedId.class )
|| returnedClass.isAnnotationPresent( Embeddable.class );
//FIXME do the overrideColumnFromMapsIdProperty here and force the idclass type to look like an @embedded
//Overrides from @MapsId if needed
boolean isOverridden = false;
if ( isId || propertyHolder.isOrWithinEmbeddedId() ) {
Ejb3Column[] oldColumns = columns;
columns = columnsBuilder.overrideColumnFromMapsIdProperty(isId);
isOverridden = oldColumns != columns;
}
if ( isComponent ) {
String referencedEntityName = null;
if (isOverridden) {
final PropertyData mapsIdProperty = BinderHelper.getPropertyAnnotatedWithMapsId(
isId, propertyHolder, property.getName(), mappings
);
referencedEntityName = mapsIdProperty.getClassOrElementName();
}
AccessType propertyAccessor = entityBinder.getPropertyAccessor( property );
propertyBinder = bindComponent(
inferredData,
@ -1850,7 +1785,9 @@ public final class AnnotationBinder {
mappings,
isComponentEmbedded,
isId,
inheritanceStatePerClass
inheritanceStatePerClass,
referencedEntityName,
isOverridden ? (Ejb3JoinColumn[]) columns : null
);
}
else {
@ -1870,16 +1807,6 @@ public final class AnnotationBinder {
}
}
//Override from @MapsId if needed
if ( isId || propertyHolder.isOrWithinEmbeddedId() ) {
columns = overrideColumnFromMapsIdProperty(
isId ? "" : property.getName(), //@MapsId("") points to the id property
columns,
propertyHolder,
entityBinder,
mappings );
}
propertyBinder.setLazy( lazy );
propertyBinder.setColumns( columns );
// if ( isIdentifierMapper ) {
@ -1968,69 +1895,6 @@ public final class AnnotationBinder {
);
}
private static Ejb3JoinColumn[] buildDefaultJoinColumnsForXToOne(PropertyHolder propertyHolder, XProperty property, PropertyData inferredData, EntityBinder entityBinder, ExtendedMappings mappings) {
Ejb3JoinColumn[] joinColumns;
JoinTable joinTableAnn = propertyHolder.getJoinTable( property );
if ( joinTableAnn != null ) {
joinColumns = Ejb3JoinColumn.buildJoinColumns(
joinTableAnn.inverseJoinColumns(), null, entityBinder.getSecondaryTables(),
propertyHolder, inferredData.getPropertyName(), mappings
);
if ( StringHelper.isEmpty( joinTableAnn.name() ) ) {
throw new AnnotationException(
"JoinTable.name() on a @ToOne association has to be explicit: "
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
}
else {
OneToOne oneToOneAnn = property.getAnnotation( OneToOne.class );
String mappedBy = oneToOneAnn != null ?
oneToOneAnn.mappedBy() :
null;
joinColumns = Ejb3JoinColumn.buildJoinColumns(
( JoinColumn[]) null,
mappedBy, entityBinder.getSecondaryTables(),
propertyHolder, inferredData.getPropertyName(), mappings
);
}
return joinColumns;
}
private static Ejb3JoinColumn[] buildExplicitJoinColumns(PropertyHolder propertyHolder, XProperty property, PropertyData inferredData, EntityBinder entityBinder, ExtendedMappings mappings) {
//process @JoinColumn(s) before @Column(s) to handle collection of entities properly
Ejb3JoinColumn[] joinColumns = null;
{
JoinColumn[] anns = null;
if ( property.isAnnotationPresent( JoinColumn.class ) ) {
anns = new JoinColumn[] { property.getAnnotation( JoinColumn.class ) };
}
else if ( property.isAnnotationPresent( JoinColumns.class ) ) {
JoinColumns ann = property.getAnnotation( JoinColumns.class );
anns = ann.value();
int length = anns.length;
if ( length == 0 ) {
throw new AnnotationException( "Cannot bind an empty @JoinColumns" );
}
}
if ( anns != null ) {
joinColumns = Ejb3JoinColumn.buildJoinColumns(
anns, null, entityBinder.getSecondaryTables(),
propertyHolder, inferredData.getPropertyName(), mappings
);
}
else if ( property.isAnnotationPresent( JoinColumnsOrFormulas.class ) ) {
JoinColumnsOrFormulas ann = property.getAnnotation( JoinColumnsOrFormulas.class );
joinColumns = Ejb3JoinColumn.buildJoinColumnsOrFormulas(
ann, null, entityBinder.getSecondaryTables(),
propertyHolder, inferredData.getPropertyName(), mappings
);
}
}
return joinColumns;
}
//TODO move that to collection binder?
private static void bindJoinedTableAssociation(
XProperty property, ExtendedMappings mappings, EntityBinder entityBinder,
@ -2107,14 +1971,27 @@ public final class AnnotationBinder {
boolean isIdentifierMapper,
ExtendedMappings mappings,
boolean isComponentEmbedded,
boolean isId,
Map<XClass, InheritanceState> inheritanceStatePerClass
) {
Component comp = fillComponent(
propertyHolder, inferredData, propertyAccessor, !isId, entityBinder,
isComponentEmbedded, isIdentifierMapper,
false, mappings, inheritanceStatePerClass
);
boolean isId, //is a identifier
Map<XClass, InheritanceState> inheritanceStatePerClass,
String referencedEntityName, //is a component who is overridden by a @MapsId
Ejb3JoinColumn[] columns) {
Component comp;
if ( referencedEntityName != null ) {
comp = createComponent( propertyHolder, inferredData, isComponentEmbedded, isIdentifierMapper );
SecondPass sp = new CopyIdentifierComponentSecondPass(
comp,
referencedEntityName,
columns,
mappings);
mappings.addSecondPass( sp );
}
else {
comp = fillComponent(
propertyHolder, inferredData, propertyAccessor, !isId, entityBinder,
isComponentEmbedded, isIdentifierMapper,
false, mappings, inheritanceStatePerClass
);
}
if (isId) {
comp.setKey( true );
if ( propertyHolder.getPersistentClass().getIdentifier() != null ) {
@ -2341,32 +2218,6 @@ public final class AnnotationBinder {
}
}
private static Ejb3Column[] overrideColumnFromMapsIdProperty(String propertyPath,
Ejb3Column[] columns,
PropertyHolder propertyHolder,
EntityBinder entityBinder,
ExtendedMappings mappings) {
Ejb3Column[] result = columns;
final XClass persistentXClass;
try {
persistentXClass = mappings.getReflectionManager()
.classForName( propertyHolder.getPersistentClass().getClassName(), AnnotationBinder.class );
}
catch ( ClassNotFoundException e ) {
throw new AssertionFailure( "PersistentClass name cannot be converted into a Class", e);
}
final PropertyData annotatedWithMapsId = mappings.getPropertyAnnotatedWithMapsId( persistentXClass, propertyPath );
if ( annotatedWithMapsId != null ) {
result = buildExplicitJoinColumns( propertyHolder, annotatedWithMapsId.getProperty(), annotatedWithMapsId, entityBinder, mappings );
if (result == null) {
result = buildDefaultJoinColumnsForXToOne( propertyHolder, annotatedWithMapsId.getProperty(), annotatedWithMapsId, entityBinder, mappings );
throw new UnsupportedOperationException( "Implicit @JoinColumn is not supported on @MapsId properties: "
+ annotatedWithMapsId.getDeclaringClass() + " " + annotatedWithMapsId.getPropertyName() );
}
}
return result;
}
private static void setupComponentTuplizer(XProperty property, Component component) {
if ( property == null ) return;
if ( property.isAnnotationPresent( Tuplizers.class ) ) {
@ -2459,9 +2310,7 @@ public final class AnnotationBinder {
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() );
propertyBinder.makePropertyAndBind();
}
protected static void defineFetchingStrategy(ToOne toOne, XProperty property) {
@ -2768,14 +2617,17 @@ public final class AnnotationBinder {
* @param orderedClasses Order list of all annotated entities and their mapped superclasses
* @return A map of {@code InheritanceState}s keyed against their {@code XClass}.
*/
public static Map<XClass, InheritanceState> buildInheritanceStates(List<XClass> orderedClasses) {
public static Map<XClass, InheritanceState> buildInheritanceStates(
List<XClass> orderedClasses,
ExtendedMappings mappings) {
ReflectionManager reflectionManager = mappings.getReflectionManager();
Map<XClass, InheritanceState> inheritanceStatePerClass = new HashMap<XClass, InheritanceState>(
orderedClasses.size()
);
for (XClass clazz : orderedClasses) {
InheritanceState superclassState = InheritanceState.getSuperclassInheritanceState(
clazz, inheritanceStatePerClass );
InheritanceState state = new InheritanceState( clazz );
InheritanceState state = new InheritanceState( clazz, inheritanceStatePerClass, mappings );
if ( superclassState != null ) {
//the classes are ordered thus preventing an NPE
//FIXME if an entity has subclasses annotated @MappedSperclass wo sub @Entity this is wrong
@ -2827,4 +2679,5 @@ public final class AnnotationBinder {
}
return false;
}
}

View File

@ -359,35 +359,11 @@ public class AnnotationConfiguration extends Configuration {
caches.clear();
try {
inSecondPass = true;
Iterator iter = secondPasses.iterator();
while ( iter.hasNext() ) {
SecondPass sp = ( SecondPass ) iter.next();
//do the second pass of simple value types first and remove them
if ( sp instanceof SetSimpleValueTypeSecondPass ) {
sp.doSecondPass( classes );
iter.remove();
}
}
processSecondPassesOfType( SetSimpleValueTypeSecondPass.class );
processSecondPassesOfType( CopyIdentifierComponentSecondPass.class );
processFkSecondPassInOrder();
iter = secondPasses.iterator();
while ( iter.hasNext() ) {
SecondPass sp = ( SecondPass ) iter.next();
//do the second pass of fk before the others and remove them
if ( sp instanceof CreateKeySecondPass ) {
sp.doSecondPass( classes );
iter.remove();
}
}
iter = secondPasses.iterator();
while ( iter.hasNext() ) {
SecondPass sp = ( SecondPass ) iter.next();
//do the SecondaryTable second pass before any association because associations can be built on joins
if ( sp instanceof SecondaryTableSecondPass ) {
sp.doSecondPass( classes );
iter.remove();
}
}
processSecondPassesOfType( CreateKeySecondPass.class );
processSecondPassesOfType( SecondaryTableSecondPass.class );
super.secondPassCompile();
inSecondPass = false;
}
@ -413,6 +389,18 @@ public class AnnotationConfiguration extends Configuration {
applyConstraintsToDDL();
}
private void processSecondPassesOfType(Class<? extends SecondPass> type) {
Iterator iter = secondPasses.iterator();
while ( iter.hasNext() ) {
SecondPass sp = ( SecondPass ) iter.next();
//do the second pass of simple value types first and remove them
if ( type.isInstance( sp ) ) {
sp.doSecondPass( classes );
iter.remove();
}
}
}
private void applyConstraintsToDDL() {
boolean applyOnDdl = getProperties().getProperty(
"hibernate.validator.apply_to_ddl",
@ -623,10 +611,11 @@ public class AnnotationConfiguration extends Configuration {
log.debug( "Process annotated classes" );
//bind classes in the correct order calculating some inheritance state
List<XClass> orderedClasses = orderAndFillHierarchy( annotatedClasses );
Map<XClass, InheritanceState> inheritanceStatePerClass = AnnotationBinder.buildInheritanceStates(
orderedClasses
);
ExtendedMappings mappings = createExtendedMappings();
Map<XClass, InheritanceState> inheritanceStatePerClass = AnnotationBinder.buildInheritanceStates(
orderedClasses, mappings
);
for ( XClass clazz : orderedClasses ) {
//todo use the same extended mapping

View File

@ -652,4 +652,17 @@ public class BinderHelper {
return StringHelper.qualify( holder.getPath(), property.getPropertyName() );
}
static PropertyData getPropertyAnnotatedWithMapsId(boolean isId, PropertyHolder propertyHolder, String propertyName, ExtendedMappings mappings) {
final XClass persistentXClass;
try {
persistentXClass = mappings.getReflectionManager()
.classForName( propertyHolder.getPersistentClass().getClassName(), AnnotationBinder.class );
}
catch ( ClassNotFoundException e ) {
throw new AssertionFailure( "PersistentClass name cannot be converted into a Class", e);
}
String propertyPath = isId ? "" : propertyName;
final PropertyData annotatedWithMapsId = mappings.getPropertyAnnotatedWithMapsId( persistentXClass, propertyPath );
return annotatedWithMapsId;
}
}

View File

@ -0,0 +1,199 @@
package org.hibernate.cfg;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.annotations.CollectionOfElements;
import org.hibernate.annotations.Columns;
import org.hibernate.annotations.Formula;
import org.hibernate.annotations.JoinColumnsOrFormulas;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.cfg.annotations.Nullability;
import org.hibernate.util.StringHelper;
/**
* Do the initial discovery of columns metadata and apply defaults.
* Also hosts some convinient methods related to column processing
*
* @author Emmanuel Bernard
*/
class ColumnsBuilder {
private PropertyHolder propertyHolder;
private Nullability nullability;
private XProperty property;
private PropertyData inferredData;
private EntityBinder entityBinder;
private ExtendedMappings mappings;
private Ejb3Column[] columns;
private Ejb3JoinColumn[] joinColumns;
public ColumnsBuilder(PropertyHolder propertyHolder, Nullability nullability, XProperty property, PropertyData inferredData, EntityBinder entityBinder, ExtendedMappings mappings) {
this.propertyHolder = propertyHolder;
this.nullability = nullability;
this.property = property;
this.inferredData = inferredData;
this.entityBinder = entityBinder;
this.mappings = mappings;
}
public Ejb3Column[] getColumns() {
return columns;
}
public Ejb3JoinColumn[] getJoinColumns() {
return joinColumns;
}
public ColumnsBuilder extractMetadata() {
columns = null;
joinColumns = buildExplicitJoinColumns(property, inferredData);
if ( property.isAnnotationPresent( Column.class ) || property.isAnnotationPresent( Formula.class ) ) {
Column ann = property.getAnnotation( Column.class );
Formula formulaAnn = property.getAnnotation( Formula.class );
columns = Ejb3Column.buildColumnFromAnnotation(
new Column[] { ann }, formulaAnn, nullability, propertyHolder, inferredData,
entityBinder.getSecondaryTables(), mappings
);
}
else if ( property.isAnnotationPresent( Columns.class ) ) {
Columns anns = property.getAnnotation( Columns.class );
columns = Ejb3Column.buildColumnFromAnnotation(
anns.columns(), null,
nullability, propertyHolder, inferredData, entityBinder.getSecondaryTables(),
mappings
);
}
//set default values if needed
if ( joinColumns == null &&
( property.isAnnotationPresent( ManyToOne.class )
|| property.isAnnotationPresent( OneToOne.class ) )
) {
joinColumns = buildDefaultJoinColumnsForXToOne(property, inferredData);
}
else if ( joinColumns == null &&
( property.isAnnotationPresent( OneToMany.class )
|| property.isAnnotationPresent( CollectionOfElements.class ) //legacy Hibernate
|| property.isAnnotationPresent( ElementCollection.class )
) ) {
OneToMany oneToMany = property.getAnnotation( OneToMany.class );
String mappedBy = oneToMany != null ?
oneToMany.mappedBy() :
"";
joinColumns = Ejb3JoinColumn.buildJoinColumns(
null,
mappedBy, entityBinder.getSecondaryTables(),
propertyHolder, inferredData.getPropertyName(), mappings
);
}
else if ( joinColumns == null && property.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) {
throw new AnnotationException( "@Any requires an explicit @JoinColumn(s): "
+ BinderHelper.getPath( propertyHolder, inferredData ) );
}
if ( columns == null && !property.isAnnotationPresent( ManyToMany.class ) ) {
//useful for collection of embedded elements
columns = Ejb3Column.buildColumnFromAnnotation(
null, null,
nullability, propertyHolder, inferredData, entityBinder.getSecondaryTables(), mappings
);
}
if ( nullability == Nullability.FORCED_NOT_NULL ) {
//force columns to not null
for (Ejb3Column col : columns ) {
col.forceNotNull();
}
}
return this;
}
Ejb3JoinColumn[] buildDefaultJoinColumnsForXToOne(XProperty property, PropertyData inferredData) {
Ejb3JoinColumn[] joinColumns;
JoinTable joinTableAnn = propertyHolder.getJoinTable( property );
if ( joinTableAnn != null ) {
joinColumns = Ejb3JoinColumn.buildJoinColumns(
joinTableAnn.inverseJoinColumns(), null, entityBinder.getSecondaryTables(),
propertyHolder, inferredData.getPropertyName(), mappings
);
if ( StringHelper.isEmpty( joinTableAnn.name() ) ) {
throw new AnnotationException(
"JoinTable.name() on a @ToOne association has to be explicit: "
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
}
else {
OneToOne oneToOneAnn = property.getAnnotation( OneToOne.class );
String mappedBy = oneToOneAnn != null ?
oneToOneAnn.mappedBy() :
null;
joinColumns = Ejb3JoinColumn.buildJoinColumns(
null,
mappedBy, entityBinder.getSecondaryTables(),
propertyHolder, inferredData.getPropertyName(), mappings
);
}
return joinColumns;
}
Ejb3JoinColumn[] buildExplicitJoinColumns(XProperty property, PropertyData inferredData) {
//process @JoinColumn(s) before @Column(s) to handle collection of entities properly
Ejb3JoinColumn[] joinColumns = null;
{
JoinColumn[] anns = null;
if ( property.isAnnotationPresent( JoinColumn.class ) ) {
anns = new JoinColumn[] { property.getAnnotation( JoinColumn.class ) };
}
else if ( property.isAnnotationPresent( JoinColumns.class ) ) {
JoinColumns ann = property.getAnnotation( JoinColumns.class );
anns = ann.value();
int length = anns.length;
if ( length == 0 ) {
throw new AnnotationException( "Cannot bind an empty @JoinColumns" );
}
}
if ( anns != null ) {
joinColumns = Ejb3JoinColumn.buildJoinColumns(
anns, null, entityBinder.getSecondaryTables(),
propertyHolder, inferredData.getPropertyName(), mappings
);
}
else if ( property.isAnnotationPresent( JoinColumnsOrFormulas.class ) ) {
JoinColumnsOrFormulas ann = property.getAnnotation( JoinColumnsOrFormulas.class );
joinColumns = Ejb3JoinColumn.buildJoinColumnsOrFormulas(
ann, null, entityBinder.getSecondaryTables(),
propertyHolder, inferredData.getPropertyName(), mappings
);
}
}
return joinColumns;
}
Ejb3Column[] overrideColumnFromMapsIdProperty(boolean isId) {
Ejb3Column[] result = columns;
final PropertyData annotatedWithMapsId = BinderHelper.getPropertyAnnotatedWithMapsId( isId, propertyHolder, property.getName(), mappings );
if ( annotatedWithMapsId != null ) {
result = buildExplicitJoinColumns( annotatedWithMapsId.getProperty(), annotatedWithMapsId );
if (result == null) {
//result = buildDefaultJoinColumnsForXToOne( annotatedWithMapsId.getProperty(), annotatedWithMapsId);
throw new UnsupportedOperationException( "Implicit @JoinColumn is not supported on @MapsId properties: "
+ annotatedWithMapsId.getDeclaringClass() + " " + annotatedWithMapsId.getPropertyName() );
}
}
return result;
}
}

View File

@ -0,0 +1,118 @@
package org.hibernate.cfg;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.MappingException;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Value;
/**
* @author Emmanuel Bernard
*/
public class CopyIdentifierComponentSecondPass implements SecondPass {
private final String referencedEntityName;
private final Component component;
private final ExtendedMappings mappings;
private final Ejb3JoinColumn[] joinColumns;
public CopyIdentifierComponentSecondPass(
Component comp, String referencedEntityName, Ejb3JoinColumn[] joinColumns, ExtendedMappings mappings) {
this.component = comp;
this.referencedEntityName = referencedEntityName;
this.mappings = mappings;
this.joinColumns = joinColumns;
}
//FIXME better error names
public void doSecondPass(Map persistentClasses) throws MappingException {
PersistentClass referencedPersistentClass = (PersistentClass) persistentClasses.get( referencedEntityName );
if ( referencedPersistentClass == null ) {
throw new AnnotationException(
"Unknown entity name: " + referencedEntityName
);
};
if ( ! ( referencedPersistentClass.getIdentifier() instanceof Component ) ) {
throw new AssertionFailure( "Unexpected identifier type on the referenced entity when mapping a @MapsId: "
+ referencedEntityName);
}
Component referencedComponent = (Component) referencedPersistentClass.getIdentifier();
Iterator<Property> properties = referencedComponent.getPropertyIterator();
//prepare column name structure
boolean isExplicitReference = true;
Map<String, Ejb3JoinColumn> columnByReferencedName = new HashMap<String, Ejb3JoinColumn>(joinColumns.length);
for (Ejb3JoinColumn joinColumn : joinColumns) {
final String referencedColumnName = joinColumn.getReferencedColumn();
if ( BinderHelper.isDefault( referencedColumnName ) ) {
break;
}
columnByReferencedName.put( referencedColumnName, joinColumn );
}
//try default column orientation
int index = 0;
if ( columnByReferencedName.isEmpty() ) {
isExplicitReference = false;
for (Ejb3JoinColumn joinColumn : joinColumns) {
columnByReferencedName.put( "" + index, joinColumn );
index++;
}
index = 0;
}
while ( properties.hasNext() ) {
Property referencedProperty = properties.next();
if ( referencedProperty.isComposite() ) {
throw new AssertionFailure( "Unexpected nested component on the referenced entity when mapping a @MapsId: "
+ referencedEntityName);
}
else {
Property property = new Property();
property.setName( referencedProperty.getName() );
property.setNodeName( referencedProperty.getNodeName() );
//FIXME set optional?
//property.setOptional( property.isOptional() );
property.setPersistentClass( component.getOwner() );
property.setPropertyAccessorName( referencedProperty.getPropertyAccessorName() );
SimpleValue value = new SimpleValue( component.getTable() );
property.setValue( value );
final SimpleValue referencedValue = (SimpleValue) referencedProperty.getValue();
value.setTypeName( referencedValue.getTypeName() );
value.setTypeParameters( referencedValue.getTypeParameters() );
final Iterator<Column> columns = referencedValue.getColumnIterator();
//FIXME take care of Formula
while ( columns.hasNext() ) {
Column column = columns.next();
final Ejb3JoinColumn joinColumn;
String logicalColumnName = null;
if ( isExplicitReference ) {
final String columnName = column.getName();
logicalColumnName = mappings.getLogicalColumnName( columnName, referencedPersistentClass.getTable() );
joinColumn = columnByReferencedName.get( logicalColumnName );
}
else {
joinColumn = columnByReferencedName.get( "" + index );
index++;
}
if (joinColumn == null) {
throw new AnnotationException(
isExplicitReference ?
"Unable to find column reference in the @MapsId mapping: " + logicalColumnName :
"Implicit column reference in the @MapsId mapping fails, try to use explicit referenceColumnNames: " + referencedEntityName
);
}
value.addColumn( new Column( joinColumn.getName() ) );
column.setValue( value );
}
component.addProperty( property );
}
}
}
}

View File

@ -23,13 +23,24 @@
*/
package org.hibernate.cfg;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.MappedSuperclass;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.mapping.PersistentClass;
/**
* Some extra data to the inheritance position of a class.
@ -37,12 +48,8 @@ import org.hibernate.annotations.common.reflection.XClass;
* @author Emmanuel Bernard
*/
public class InheritanceState {
public InheritanceState(XClass clazz) {
this.setClazz( clazz );
extractInheritanceType();
}
private XClass clazz;
private XClass identifierType;
/**
* Has sibling (either mappedsuperclass entity)
@ -55,6 +62,21 @@ public class InheritanceState {
private boolean hasParents = false;
private InheritanceType type;
private boolean isEmbeddableSuperclass = false;
private Map<XClass, InheritanceState> inheritanceStatePerClass;
private List<XClass> classesToProcessForMappedSuperclass = new ArrayList<XClass>();
private ExtendedMappings mappings;
private AccessType accessType;
private ElementsToProcess elementsToProcess;
public InheritanceState(XClass clazz,
Map<XClass, InheritanceState> inheritanceStatePerClass,
ExtendedMappings mappings) {
this.setClazz( clazz );
this.mappings = mappings;
this.inheritanceStatePerClass = inheritanceStatePerClass;
this.identifierType = mappings.getReflectionManager().toXClass( void.class ); //not initialized
extractInheritanceType();
}
private void extractInheritanceType() {
XAnnotatedElement element = getClazz();
@ -144,4 +166,170 @@ public class InheritanceState {
public void setEmbeddableSuperclass(boolean embeddableSuperclass) {
isEmbeddableSuperclass = embeddableSuperclass;
}
public XClass getIdentifierTypeIfComponent() {
final ReflectionManager reflectionManager = mappings.getReflectionManager();
if ( reflectionManager.equals( identifierType, void.class ) ) {
IdClass idClass = clazz.getAnnotation( IdClass.class );
if (idClass != null) {
identifierType = reflectionManager.toXClass( idClass.value() );
}
else {
//find @EmbeddedId
getElementsToProcess();
}
}
return identifierType;
}
void postProcess(PersistentClass persistenceClass, EntityBinder entityBinder) {
//make sure we run elements to process
getElementsToProcess();
addMappedSuperClassInMetadata(persistenceClass);
entityBinder.setPropertyAccessType( accessType );
}
public XClass getClassWithIdClass(boolean evenIfSubclass) {
if ( !evenIfSubclass && hasParents() ) {
return null;
}
if ( clazz.isAnnotationPresent( IdClass.class ) ) {
return clazz;
}
else {
InheritanceState state = InheritanceState.getSuperclassInheritanceState( clazz, inheritanceStatePerClass );
if (state != null){
return state.getClassWithIdClass(true);
}
else {
return null;
}
}
}
/*
* Get the annotated elements, guessing the access type from @Id or @EmbeddedId presence.
* Change EntityBinder by side effect
*/
public ElementsToProcess getElementsToProcess() {
if (elementsToProcess == null) {
InheritanceState inheritanceState = inheritanceStatePerClass.get( clazz );
assert !inheritanceState.isEmbeddableSuperclass();
getMappedSuperclassesTillNextEntityOrdered();
accessType = determineDefaultAccessType( );
List<PropertyData> elements = new ArrayList<PropertyData>();
int deep = classesToProcessForMappedSuperclass.size();
int idPropertyCount = 0;
for ( int index = 0; index < deep; index++ ) {
PropertyContainer propertyContainer = new PropertyContainer( classesToProcessForMappedSuperclass.get( index ), clazz );
int currentIdPropertyCount = AnnotationBinder.addElementsOfClass( elements, accessType, propertyContainer, mappings );
idPropertyCount += currentIdPropertyCount;
}
if ( idPropertyCount == 0 && !inheritanceState.hasParents() ) {
throw new AnnotationException( "No identifier specified for entity: " + clazz.getName() );
}
elementsToProcess = new ElementsToProcess( elements, idPropertyCount);
}
return elementsToProcess;
}
private AccessType determineDefaultAccessType() {
XClass xclass = clazz;
while ( xclass != null && !Object.class.getName().equals( xclass.getName() ) ) {
if ( xclass.isAnnotationPresent( Entity.class ) || xclass.isAnnotationPresent( MappedSuperclass.class ) ) {
for ( XProperty prop : xclass.getDeclaredProperties( AccessType.PROPERTY.getType() ) ) {
final boolean isEmbeddedId = prop.isAnnotationPresent( EmbeddedId.class );
if ( prop.isAnnotationPresent( Id.class ) || isEmbeddedId ) {
if (isEmbeddedId) {
identifierType = prop.getClassOrElementClass();
}
return AccessType.PROPERTY;
}
}
for ( XProperty prop : xclass.getDeclaredProperties( AccessType.FIELD.getType() ) ) {
final boolean isEmbeddedId = prop.isAnnotationPresent( EmbeddedId.class );
if ( prop.isAnnotationPresent( Id.class ) || isEmbeddedId ) {
if ( isEmbeddedId ) {
identifierType = prop.getClassOrElementClass();
}
return AccessType.FIELD;
}
}
}
xclass = xclass.getSuperclass();
}
throw new AnnotationException( "No identifier specified for entity: " + clazz.getName() );
}
private void getMappedSuperclassesTillNextEntityOrdered() {
//ordered to allow proper messages on properties subclassing
XClass currentClassInHierarchy = clazz;
InheritanceState superclassState;
do {
classesToProcessForMappedSuperclass.add( 0, currentClassInHierarchy );
XClass superClass = currentClassInHierarchy;
do {
superClass = superClass.getSuperclass();
superclassState = inheritanceStatePerClass.get( superClass );
}
while ( superClass != null
&& !mappings.getReflectionManager().equals( superClass, Object.class ) && superclassState == null );
currentClassInHierarchy = superClass;
}
while ( superclassState != null && superclassState.isEmbeddableSuperclass() );
}
private void addMappedSuperClassInMetadata(PersistentClass persistentClass) {
//add @MappedSuperclass in the metadata
// classes from 0 to n-1 are @MappedSuperclass and should be linked
org.hibernate.mapping.MappedSuperclass mappedSuperclass = null;
final InheritanceState superEntityState =
InheritanceState.getInheritanceStateOfSuperEntity( clazz, inheritanceStatePerClass );
PersistentClass superEntity =
superEntityState != null ?
mappings.getClass( superEntityState.getClazz().getName() ) :
null;
final int lastMappedSuperclass = classesToProcessForMappedSuperclass.size() - 1;
for ( int index = 0 ; index < lastMappedSuperclass ; index++ ) {
org.hibernate.mapping.MappedSuperclass parentSuperclass = mappedSuperclass;
final Class<?> type = mappings.getReflectionManager().toClass( classesToProcessForMappedSuperclass.get( index ) );
//add MAppedSuperclass if not already there
mappedSuperclass = mappings.getMappedSuperclass( type );
if (mappedSuperclass == null) {
mappedSuperclass = new org.hibernate.mapping.MappedSuperclass(parentSuperclass, superEntity );
mappedSuperclass.setMappedClass( type );
mappings.addMappedSuperclass( type, mappedSuperclass );
}
}
if (mappedSuperclass != null) {
persistentClass.setSuperMappedSuperclass(mappedSuperclass);
}
}
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;
}
}
}

View File

@ -7,7 +7,8 @@ import org.hibernate.test.util.SchemaUtil;
/**
* @author Emmanuel Bernard
*/
public class DerivedIdentitySimpleParentEmbeddedIdDepTest extends TestCase {
public class
DerivedIdentitySimpleParentEmbeddedIdDepTest extends TestCase {
public void testManyToOne() throws Exception {
assertTrue( SchemaUtil.isColumnPresent( "Dependent", "FK", getCfg() ) );

View File

@ -17,7 +17,6 @@ public class Dependent implements Serializable {
@Id
//@JoinColumn(name = "FK")
// id attribute mapped by join column default
@ManyToOne
Employee emp;

View File

@ -2,6 +2,7 @@ package org.hibernate.test.annotations.derivedidentities.e2.b;
import java.io.Serializable;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
/**
* @author Emmanuel Bernard

View File

@ -0,0 +1,49 @@
package org.hibernate.test.annotations.derivedidentities.e2.b;
import org.hibernate.Session;
import org.hibernate.test.annotations.TestCase;
import org.hibernate.test.util.SchemaUtil;
/**
* @author Emmanuel Bernard
*/
public class DerivedIdentityIdClassParentEmbeddedIdDepTest extends TestCase {
public void testManyToOne() throws Exception {
assertTrue( SchemaUtil.isColumnPresent( "Dependent", "FK1", getCfg() ) );
assertTrue( SchemaUtil.isColumnPresent( "Dependent", "FK2", getCfg() ) );
assertTrue( SchemaUtil.isColumnPresent( "Dependent", "name", getCfg() ) );
assertTrue( ! SchemaUtil.isColumnPresent( "Dependent", "firstName", getCfg() ) );
assertTrue( ! SchemaUtil.isColumnPresent( "Dependent", "lastName", getCfg() ) );
Employee e = new Employee();
e.firstName = "Emmanuel";
e.lastName = "Bernard";
Session s = openSession( );
s.getTransaction().begin();
s.persist( e );
Dependent d = new Dependent();
d.emp = e;
d.id = new DependentId();
d.id.name = "Doggy";
d.id.empPK = new EmployeeId();
d.id.empPK.firstName = e.firstName; //FIXME not needed when foreign is enabled
d.id.empPK.lastName = e.lastName; //FIXME not needed when foreign is enabled
s.persist( d );
s.flush();
s.clear();
d = (Dependent) s.get( Dependent.class, d.id );
assertNotNull( d.emp );
assertEquals( e.firstName, d.emp.firstName );
s.getTransaction().rollback();
s.close();
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Dependent.class,
Employee.class
};
}
}

View File

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