org.hibernate.boot.model.source.internal.hbm.EntityHierarchyBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-core Show documentation
Show all versions of hibernate-core Show documentation
Hibernate's core ORM functionality
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or .
*/
package org.hibernate.boot.model.source.internal.hbm;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmDiscriminatorSubclassEntityType;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmEntityBaseDefinition;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmHibernateMapping;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmJoinedSubclassEntityType;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmRootEntityType;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmSubclassEntityBaseDefinition;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmUnionSubclassEntityType;
import org.hibernate.boot.model.source.spi.EntitySource;
import org.hibernate.internal.util.StringHelper;
import org.jboss.logging.Logger;
/**
* Helper for dealing with entity inheritance hierarchies in {@code hbm.xml}
* processing. In {@code hbm.xml} the super/sub may be split across documents.
* The purpose of this class is to:
* -
* validate that all hierarchies are complete (make sure a mapping does not reference
* an unknown entity as its super)
*
* -
* ultimately order the processing of every entity to make sure we process each
* hierarchy "downward" (from super to sub(s)).
*
*
*
* @author Steve Ebersole
*/
public class EntityHierarchyBuilder {
private static final Logger log = Logger.getLogger( EntityHierarchyBuilder.class );
private final List entityHierarchyList = new ArrayList();
private final Map entitySourceByNameMap = new HashMap();
private Map> toBeLinkedQueue;
public EntityHierarchyBuilder() {
}
/**
* To be called after all mapping documents have been processed (via {@link #indexMappingDocument})
*
* @return The built hierarchies
*
* @throws HibernateException Indicates subclass mappings still waiting to be linked to their super types
*/
public List buildHierarchies() throws HibernateException {
if ( toBeLinkedQueue != null && !toBeLinkedQueue.isEmpty() ) {
if ( log.isDebugEnabled() ) {
for ( Map.Entry> waitingListEntry : toBeLinkedQueue.entrySet() ) {
for ( ExtendsQueueEntry waitingEntry : waitingListEntry.getValue() ) {
log.debugf(
"Entity super-type named as extends [%s] for subclass [%s:%s] not found",
waitingListEntry.getKey(),
waitingEntry.sourceMappingDocument.getOrigin(),
waitingEntry.sourceMappingDocument.determineEntityName( waitingEntry.jaxbSubEntityMapping )
);
}
}
}
throw new HibernateException(
"Not all named super-types (extends) were found : " + toBeLinkedQueue.keySet()
);
}
return entityHierarchyList;
}
/**
* Called for each mapping document.
*
* @param mappingDocument The {@code hbm.xml} document to index
*/
public void indexMappingDocument(MappingDocument mappingDocument) {
log.tracef( "Indexing mapping document [%s] for purpose of building entity hierarchy ordering", mappingDocument.getOrigin() );
final JaxbHbmHibernateMapping mappingBinding = mappingDocument.getDocumentRoot();
// iterate all root class definitions at the hibernate-mapping level
for ( JaxbHbmRootEntityType jaxbRootEntity : mappingBinding.getClazz() ) {
// we can immediately handle elements in terms of creating the hierarchy entry
final RootEntitySourceImpl rootEntitySource = new RootEntitySourceImpl( mappingDocument, jaxbRootEntity );
entitySourceByNameMap.put( rootEntitySource.getEntityNamingSource().getEntityName(), rootEntitySource );
final EntityHierarchySourceImpl hierarchy = new EntityHierarchySourceImpl( rootEntitySource );
entityHierarchyList.add( hierarchy );
linkAnyWaiting( mappingDocument, rootEntitySource );
// process any of its nested sub-entity definitions
processRootEntitySubEntityElements( mappingDocument, jaxbRootEntity, rootEntitySource );
}
// iterate all discriminator-based subclass definitions at the hibernate-mapping level
for ( JaxbHbmDiscriminatorSubclassEntityType discriminatorSubclassEntityBinding : mappingBinding.getSubclass() ) {
processTopLevelSubClassBinding( mappingDocument, discriminatorSubclassEntityBinding );
}
// iterate all joined-subclass definitions at the hibernate-mapping level
for ( JaxbHbmJoinedSubclassEntityType joinedSubclassEntityBinding : mappingBinding.getJoinedSubclass() ) {
processTopLevelSubClassBinding( mappingDocument, joinedSubclassEntityBinding );
}
// iterate all union-subclass definitions at the hibernate-mapping level
for ( JaxbHbmUnionSubclassEntityType unionSubclassEntityBinding : mappingBinding.getUnionSubclass() ) {
processTopLevelSubClassBinding( mappingDocument, unionSubclassEntityBinding );
}
}
private void processRootEntitySubEntityElements(
MappingDocument mappingDocument,
JaxbHbmRootEntityType jaxbRootEntity,
RootEntitySourceImpl rootEntitySource) {
// todo : technically we should only allow one mutually-exclusive; should we enforce that here?
// I believe the DTD/XSD does enforce that, so maybe not a big deal
processElements( mappingDocument, jaxbRootEntity.getSubclass(), rootEntitySource );
processElements( mappingDocument, jaxbRootEntity.getJoinedSubclass(), rootEntitySource );
processElements( mappingDocument, jaxbRootEntity.getUnionSubclass(), rootEntitySource );
}
private void processSubEntityElements(
MappingDocument mappingDocument,
JaxbHbmEntityBaseDefinition entityBinding,
AbstractEntitySourceImpl container) {
if ( JaxbHbmDiscriminatorSubclassEntityType.class.isInstance( entityBinding ) ) {
final JaxbHbmDiscriminatorSubclassEntityType jaxbSubclass = (JaxbHbmDiscriminatorSubclassEntityType) entityBinding;
processElements( mappingDocument, jaxbSubclass.getSubclass(), container );
}
else if ( JaxbHbmJoinedSubclassEntityType.class.isInstance( entityBinding ) ) {
final JaxbHbmJoinedSubclassEntityType jaxbJoinedSubclass = (JaxbHbmJoinedSubclassEntityType) entityBinding;
processElements( mappingDocument, jaxbJoinedSubclass.getJoinedSubclass(), container );
}
else if ( JaxbHbmUnionSubclassEntityType.class.isInstance( entityBinding ) ) {
final JaxbHbmUnionSubclassEntityType jaxbUnionSubclass = (JaxbHbmUnionSubclassEntityType) entityBinding;
processElements( mappingDocument, jaxbUnionSubclass.getUnionSubclass(), container );
}
}
private void processElements(
MappingDocument mappingDocument,
List nestedSubEntityList,
AbstractEntitySourceImpl container) {
for ( final JaxbHbmSubclassEntityBaseDefinition jaxbSubEntity : nestedSubEntityList ) {
final SubclassEntitySourceImpl subClassEntitySource = createSubClassEntitySource(
mappingDocument,
jaxbSubEntity,
container
);
entitySourceByNameMap.put(
subClassEntitySource.getEntityNamingSource().getEntityName(),
subClassEntitySource
);
container.add( subClassEntitySource );
linkAnyWaiting( mappingDocument, subClassEntitySource );
// Re-run the sub element to handle subclasses within the subclass.
processSubEntityElements( mappingDocument, jaxbSubEntity, subClassEntitySource );
}
}
private SubclassEntitySourceImpl createSubClassEntitySource(
MappingDocument mappingDocument,
JaxbHbmSubclassEntityBaseDefinition jaxbSubEntity,
EntitySource superEntity) {
if ( JaxbHbmJoinedSubclassEntityType.class.isInstance( jaxbSubEntity ) ) {
return new JoinedSubclassEntitySourceImpl(
mappingDocument,
JaxbHbmJoinedSubclassEntityType.class.cast( jaxbSubEntity ),
superEntity
);
}
else {
return new SubclassEntitySourceImpl( mappingDocument, jaxbSubEntity, superEntity );
}
}
private void processTopLevelSubClassBinding(
MappingDocument mappingDocument,
JaxbHbmSubclassEntityBaseDefinition jaxbSubEntityMapping) {
final AbstractEntitySourceImpl entityItExtends = locateExtendedEntitySource( mappingDocument, jaxbSubEntityMapping );
if ( entityItExtends == null ) {
// we have not seen its declared super-type yet, add it to the queue to be linked up
// later when (if) we do
addToToBeLinkedQueue( mappingDocument, jaxbSubEntityMapping );
}
else {
// we have seen its super-type already
final SubclassEntitySourceImpl subEntitySource = createSubClassEntitySource(
mappingDocument,
jaxbSubEntityMapping,
entityItExtends
);
entitySourceByNameMap.put( subEntitySource.getEntityNamingSource().getEntityName(), subEntitySource );
entityItExtends.add( subEntitySource );
// this may have been a "middle type". So link any sub entities that may be waiting on it
linkAnyWaiting( mappingDocument, subEntitySource );
processSubEntityElements( mappingDocument, jaxbSubEntityMapping, subEntitySource );
}
}
private AbstractEntitySourceImpl locateExtendedEntitySource(
MappingDocument mappingDocument,
JaxbHbmSubclassEntityBaseDefinition jaxbSubEntityMapping) {
// NOTE : extends may refer to either an entity-name or a class-name, we need to check each
// first check using entity-name
AbstractEntitySourceImpl entityItExtends = entitySourceByNameMap.get( jaxbSubEntityMapping.getExtends() );
if ( entityItExtends == null ) {
// next, check using class name
entityItExtends = entitySourceByNameMap.get(
mappingDocument.qualifyClassName( jaxbSubEntityMapping.getExtends() )
);
}
return entityItExtends;
}
private void addToToBeLinkedQueue(
MappingDocument mappingDocument,
JaxbHbmSubclassEntityBaseDefinition jaxbSubEntityMapping) {
List waitingList = null;
if ( toBeLinkedQueue == null ) {
toBeLinkedQueue = new HashMap>();
}
else {
waitingList = toBeLinkedQueue.get( jaxbSubEntityMapping.getExtends() );
}
if ( waitingList == null ) {
waitingList = new ArrayList();
toBeLinkedQueue.put( jaxbSubEntityMapping.getExtends(), waitingList );
}
waitingList.add( new ExtendsQueueEntry( mappingDocument, jaxbSubEntityMapping ) );
}
private void linkAnyWaiting(
MappingDocument mappingDocument,
AbstractEntitySourceImpl entitySource) {
if ( toBeLinkedQueue == null ) {
return;
}
List waitingList = toBeLinkedQueue.remove( entitySource.jaxbEntityMapping().getEntityName() );
if ( waitingList != null ) {
processWaitingSubEntityMappings( entitySource, waitingList );
waitingList.clear();
}
if ( StringHelper.isNotEmpty( entitySource.jaxbEntityMapping().getName() ) ) {
final String entityClassName = entitySource.jaxbEntityMapping().getName();
waitingList = toBeLinkedQueue.remove( entityClassName );
if ( waitingList != null ) {
processWaitingSubEntityMappings( entitySource, waitingList );
waitingList.clear();
}
final String qualifiedEntityClassName = mappingDocument.qualifyClassName( entityClassName );
if ( !entityClassName.equals( qualifiedEntityClassName ) ) {
waitingList = toBeLinkedQueue.remove( qualifiedEntityClassName );
if ( waitingList != null ) {
processWaitingSubEntityMappings( entitySource, waitingList );
waitingList.clear();
}
}
}
}
private void processWaitingSubEntityMappings(
AbstractEntitySourceImpl entitySource,
List waitingList) {
for ( ExtendsQueueEntry entry : waitingList ) {
final SubclassEntitySourceImpl subEntitySource = createSubClassEntitySource(
entry.sourceMappingDocument,
entry.jaxbSubEntityMapping,
entitySource
);
entitySourceByNameMap.put( subEntitySource.getEntityNamingSource().getEntityName(), subEntitySource );
entitySource.add( subEntitySource );
// this may have been a "middle type". So link any sub entities that may be waiting on it
linkAnyWaiting( entry.sourceMappingDocument, subEntitySource );
processSubEntityElements( entry.sourceMappingDocument, entry.jaxbSubEntityMapping, subEntitySource );
}
}
private static class ExtendsQueueEntry {
private final MappingDocument sourceMappingDocument;
private final JaxbHbmSubclassEntityBaseDefinition jaxbSubEntityMapping;
public ExtendsQueueEntry(
MappingDocument sourceMappingDocument,
JaxbHbmSubclassEntityBaseDefinition jaxbSubEntityMapping) {
this.sourceMappingDocument = sourceMappingDocument;
this.jaxbSubEntityMapping = jaxbSubEntityMapping;
}
}
}