[hibernate-commits] Hibernate SVN: r18625 - in core/trunk/annotations/src: test/java/org/hibernate/test/annotations/derivedidentities/e1/b and 2 other directories.
hibernate-commits at lists.jboss.org
hibernate-commits at lists.jboss.org
Tue Jan 26 07:57:01 EST 2010
Author: epbernard
Date: 2010-01-26 07:57:01 -0500 (Tue, 26 Jan 2010)
New Revision: 18625
Added:
core/trunk/annotations/src/main/java/org/hibernate/cfg/ColumnsBuilder.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/CopyIdentifierComponentSecondPass.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/DerivedIdentityIdClassParentEmbeddedIdDepTest.java
Modified:
core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/InheritanceState.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/DerivedIdentitySimpleParentEmbeddedIdDepTest.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/c/Dependent.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/DependentId.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/EmployeeId.java
Log:
HHH-4529 Add support for parent's id being an IdClass or an EmbeddedId (last case not tested yet). (example e2.b)
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java 2010-01-26 12:19:38 UTC (rev 18624)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java 2010-01-26 12:57:01 UTC (rev 18625)
@@ -52,7 +52,6 @@
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.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.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 @@
* @author Hardy Ferentschik
*/
@SuppressWarnings("unchecked")
-public final class AnnotationBinder {
+public final class
+ AnnotationBinder {
/*
* Some design description
@@ -625,30 +624,16 @@
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 @@
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,134 +967,127 @@
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);
+// }
+// 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);
+// }
+// }
- 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;
- }
-
/*
* Process the filters defined on the given class, as well as all filters defined
* on the MappedSuperclass(s) in the inheritance hierarchy
@@ -1235,7 +1213,7 @@
* @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 @@
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,71 +1294,15 @@
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
- return;
+ return ;
}
- Ejb3JoinColumn[] joinColumns = buildExplicitJoinColumns(
- propertyHolder, property, inferredData, entityBinder, mappings
- );
+ ColumnsBuilder columnsBuilder = new ColumnsBuilder(
+ propertyHolder, nullability, property, inferredData, entityBinder, mappings
+ ).extractMetadata();
+ Ejb3Column[] columns = columnsBuilder.getColumns();
+ Ejb3JoinColumn[] joinColumns = columnsBuilder.getJoinColumns();
- 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(
- 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();
- }
- }
-
final XClass returnedClass = inferredData.getClassOrElement();
//prepare PropertyBinder
@@ -1839,7 +1759,22 @@
|| 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 @@
mappings,
isComponentEmbedded,
isId,
- inheritanceStatePerClass
+ inheritanceStatePerClass,
+ referencedEntityName,
+ isOverridden ? (Ejb3JoinColumn[]) columns : null
);
}
else {
@@ -1870,16 +1807,6 @@
}
}
- //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 @@
);
}
- 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 @@
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 @@
}
}
- 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 @@
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 @@
* @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 @@
}
return false;
}
+
}
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java 2010-01-26 12:19:38 UTC (rev 18624)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java 2010-01-26 12:57:01 UTC (rev 18625)
@@ -359,35 +359,11 @@
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 @@
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,11 +611,12 @@
log.debug( "Process annotated classes" );
//bind classes in the correct order calculating some inheritance state
List<XClass> orderedClasses = orderAndFillHierarchy( annotatedClasses );
+ ExtendedMappings mappings = createExtendedMappings();
Map<XClass, InheritanceState> inheritanceStatePerClass = AnnotationBinder.buildInheritanceStates(
- orderedClasses
+ orderedClasses, mappings
);
- ExtendedMappings mappings = createExtendedMappings();
+
for ( XClass clazz : orderedClasses ) {
//todo use the same extended mapping
AnnotationBinder.bindClass( clazz, inheritanceStatePerClass, mappings );
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java 2010-01-26 12:19:38 UTC (rev 18624)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java 2010-01-26 12:57:01 UTC (rev 18625)
@@ -652,4 +652,17 @@
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;
+ }
}
Added: core/trunk/annotations/src/main/java/org/hibernate/cfg/ColumnsBuilder.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/ColumnsBuilder.java (rev 0)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/ColumnsBuilder.java 2010-01-26 12:57:01 UTC (rev 18625)
@@ -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;
+ }
+}
Added: core/trunk/annotations/src/main/java/org/hibernate/cfg/CopyIdentifierComponentSecondPass.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/CopyIdentifierComponentSecondPass.java (rev 0)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/CopyIdentifierComponentSecondPass.java 2010-01-26 12:57:01 UTC (rev 18625)
@@ -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 );
+ }
+ }
+ }
+}
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/InheritanceState.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/InheritanceState.java 2010-01-26 12:19:38 UTC (rev 18624)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/InheritanceState.java 2010-01-26 12:57:01 UTC (rev 18625)
@@ -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 @@
* @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,7 +62,22 @@
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();
Inheritance inhAnn = element.getAnnotation( Inheritance.class );
@@ -144,4 +166,170 @@
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;
+ }
+ }
}
Modified: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/DerivedIdentitySimpleParentEmbeddedIdDepTest.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/DerivedIdentitySimpleParentEmbeddedIdDepTest.java 2010-01-26 12:19:38 UTC (rev 18624)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/DerivedIdentitySimpleParentEmbeddedIdDepTest.java 2010-01-26 12:57:01 UTC (rev 18625)
@@ -7,7 +7,8 @@
/**
* @author Emmanuel Bernard
*/
-public class DerivedIdentitySimpleParentEmbeddedIdDepTest extends TestCase {
+public class
+ DerivedIdentitySimpleParentEmbeddedIdDepTest extends TestCase {
public void testManyToOne() throws Exception {
assertTrue( SchemaUtil.isColumnPresent( "Dependent", "FK", getCfg() ) );
Modified: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/c/Dependent.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/c/Dependent.java 2010-01-26 12:19:38 UTC (rev 18624)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/c/Dependent.java 2010-01-26 12:57:01 UTC (rev 18625)
@@ -17,7 +17,6 @@
@Id
- //@JoinColumn(name = "FK")
// id attribute mapped by join column default
@ManyToOne
Employee emp;
Modified: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/DependentId.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/DependentId.java 2010-01-26 12:19:38 UTC (rev 18624)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/DependentId.java 2010-01-26 12:57:01 UTC (rev 18625)
@@ -2,6 +2,7 @@
import java.io.Serializable;
import javax.persistence.Embeddable;
+import javax.persistence.Embedded;
/**
* @author Emmanuel Bernard
Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/DerivedIdentityIdClassParentEmbeddedIdDepTest.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/DerivedIdentityIdClassParentEmbeddedIdDepTest.java (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/DerivedIdentityIdClassParentEmbeddedIdDepTest.java 2010-01-26 12:57:01 UTC (rev 18625)
@@ -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
+ };
+ }
+}
Modified: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/EmployeeId.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/EmployeeId.java 2010-01-26 12:19:38 UTC (rev 18624)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/EmployeeId.java 2010-01-26 12:57:01 UTC (rev 18625)
@@ -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 {
+ at Embeddable
+public class EmployeeId implements Serializable {
String firstName;
String lastName;
}
\ No newline at end of file
More information about the hibernate-commits
mailing list