/* * 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 <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.boot.model.source.internal.hbm; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import org.hibernate.EntityMode; import org.hibernate.boot.MappingException; import org.hibernate.boot.jaxb.Origin; import org.hibernate.boot.jaxb.hbm.spi.EntityInfo; import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmEntityBaseDefinition; import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmFetchProfileType; import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmFilterType; import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmNamedNativeQueryType; import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmNamedQueryType; import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmRootEntityType; import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmSecondaryTableType; import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmTuplizerType; import org.hibernate.boot.jaxb.hbm.spi.SecondaryTableContainer; import org.hibernate.boot.model.CustomSql; import org.hibernate.boot.model.TruthValue; import org.hibernate.boot.model.source.spi.AttributePath; import org.hibernate.boot.model.source.spi.AttributeRole; import org.hibernate.boot.model.source.spi.AttributeSource; import org.hibernate.boot.model.source.spi.AttributeSourceContainer; import org.hibernate.boot.model.source.spi.EntityHierarchySource; import org.hibernate.boot.model.source.spi.EntityNamingSource; import org.hibernate.boot.model.source.spi.EntitySource; import org.hibernate.boot.model.source.spi.FilterSource; import org.hibernate.boot.model.source.spi.IdentifiableTypeSource; import org.hibernate.boot.model.source.spi.JpaCallbackSource; import org.hibernate.boot.model.source.spi.LocalMetadataBuildingContext; import org.hibernate.boot.model.source.spi.NaturalIdMutability; import org.hibernate.boot.model.source.spi.SecondaryTableSource; import org.hibernate.boot.model.source.spi.SubclassEntitySource; import org.hibernate.boot.model.source.spi.ToolingHintContext; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.compare.EqualsHelper; /** * @author Steve Ebersole * @author Hardy Ferentschik * @author Brett Meyer */ public abstract class AbstractEntitySourceImpl extends AbstractHbmSourceNode implements EntitySource, Helper.InLineViewNameInferrer { private static final FilterSource[] NO_FILTER_SOURCES = new FilterSource[0]; private final JaxbHbmEntityBaseDefinition jaxbEntityMapping; private final EntityNamingSource entityNamingSource; private final AttributeRole attributeRoleBase; private final AttributePath attributePathBase; private List<IdentifiableTypeSource> subclassEntitySources = new ArrayList<IdentifiableTypeSource>(); private int inLineViewCount = 0; // logically final, but built during 'afterInstantiation' callback private List<AttributeSource> attributeSources; private Map<String,SecondaryTableSource> secondaryTableMap; private final FilterSource[] filterSources; private final Map<EntityMode, String> tuplizerClassMap; private final ToolingHintContext toolingHintContext; protected AbstractEntitySourceImpl(MappingDocument sourceMappingDocument, JaxbHbmEntityBaseDefinition jaxbEntityMapping) { super( sourceMappingDocument ); this.jaxbEntityMapping = jaxbEntityMapping; this.entityNamingSource = extractEntityNamingSource( sourceMappingDocument, jaxbEntityMapping ); this.attributePathBase = new AttributePath(); this.attributeRoleBase = new AttributeRole( entityNamingSource.getEntityName() ); this.tuplizerClassMap = extractTuplizers( jaxbEntityMapping ); this.filterSources = buildFilterSources(); for ( JaxbHbmFetchProfileType jaxbFetchProfile : jaxbEntityMapping.getFetchProfile() ) { FetchProfileBinder.processFetchProfile( sourceMappingDocument, jaxbFetchProfile, entityNamingSource.getClassName() != null ? entityNamingSource.getClassName() : entityNamingSource.getEntityName() ); } this.toolingHintContext = Helper.collectToolingHints( sourceMappingDocument.getToolingHintContext(), jaxbEntityMapping ); } public static EntityNamingSourceImpl extractEntityNamingSource( MappingDocument sourceMappingDocument, EntityInfo jaxbEntityMapping) { final String className = sourceMappingDocument.qualifyClassName( jaxbEntityMapping.getName() ); final String entityName; final String jpaEntityName; if ( StringHelper.isNotEmpty( jaxbEntityMapping.getEntityName() ) ) { entityName = jaxbEntityMapping.getEntityName(); jpaEntityName = jaxbEntityMapping.getEntityName(); } else { entityName = className; jpaEntityName = StringHelper.unqualify( className ); } return new EntityNamingSourceImpl( entityName, className, jpaEntityName ); } private static Map<EntityMode, String> extractTuplizers(JaxbHbmEntityBaseDefinition entityElement) { if ( entityElement.getTuplizer() == null ) { return Collections.emptyMap(); } final Map<EntityMode, String> tuplizers = new HashMap<EntityMode, String>(); for ( JaxbHbmTuplizerType tuplizerElement : entityElement.getTuplizer() ) { tuplizers.put( tuplizerElement.getEntityMode(), tuplizerElement.getClazz() ); } return tuplizers; } private FilterSource[] buildFilterSources() { //todo for now, i think all EntityElement should support this. if ( JaxbHbmRootEntityType.class.isInstance( jaxbEntityMapping() ) ) { final JaxbHbmRootEntityType jaxbClassElement = (JaxbHbmRootEntityType) jaxbEntityMapping(); final int size = jaxbClassElement.getFilter().size(); if ( size == 0 ) { return NO_FILTER_SOURCES; } FilterSource[] results = new FilterSource[size]; for ( int i = 0; i < size; i++ ) { JaxbHbmFilterType element = jaxbClassElement.getFilter().get( i ); results[i] = new FilterSourceImpl( sourceMappingDocument(), element ); } return results; } else { return NO_FILTER_SOURCES; } } @Override public String getXmlNodeName() { return jaxbEntityMapping.getNode(); } @Override public LocalMetadataBuildingContext getLocalMetadataBuildingContext() { return super.metadataBuildingContext(); } @Override public String getTypeName() { return entityNamingSource.getTypeName(); } @Override public AttributePath getAttributePathBase() { return attributePathBase; } @Override public AttributeRole getAttributeRoleBase() { return attributeRoleBase; } @Override public Collection<IdentifiableTypeSource> getSubTypes() { return subclassEntitySources; } @Override public FilterSource[] getFilterSources() { return filterSources; } @Override public String inferInLineViewName() { return entityNamingSource.getEntityName() + '#' + (++inLineViewCount); } protected void afterInstantiation() { this.attributeSources = buildAttributeSources(); this.secondaryTableMap = buildSecondaryTableMap(); } protected List<AttributeSource> buildAttributeSources() { final List<AttributeSource> attributeSources = new ArrayList<AttributeSource>(); AttributesHelper.Callback attributeBuildingCallback = new AttributesHelper.Callback() { @Override public AttributeSourceContainer getAttributeSourceContainer() { return AbstractEntitySourceImpl.this; } @Override public void addAttributeSource(AttributeSource attributeSource) { attributeSources.add( attributeSource ); } }; buildAttributeSources( attributeBuildingCallback ); return attributeSources; } protected void buildAttributeSources(AttributesHelper.Callback attributeBuildingCallback) { AttributesHelper.processAttributes( sourceMappingDocument(), attributeBuildingCallback, jaxbEntityMapping.getAttributes(), null, NaturalIdMutability.NOT_NATURAL_ID ); } private Map<String,SecondaryTableSource> buildSecondaryTableMap() { if ( !SecondaryTableContainer.class.isInstance( jaxbEntityMapping ) ) { return Collections.emptyMap(); } final HashMap<String,SecondaryTableSource> secondaryTableSourcesMap = new HashMap<String, SecondaryTableSource>(); for ( final JaxbHbmSecondaryTableType joinElement : ( (SecondaryTableContainer) jaxbEntityMapping ).getJoin() ) { final SecondaryTableSourceImpl secondaryTableSource = new SecondaryTableSourceImpl( sourceMappingDocument(), joinElement, getEntityNamingSource(), this ); final String logicalTableName = secondaryTableSource.getLogicalTableNameForContainedColumns(); secondaryTableSourcesMap.put( logicalTableName, secondaryTableSource ); AttributesHelper.processAttributes( sourceMappingDocument(), new AttributesHelper.Callback() { @Override public AttributeSourceContainer getAttributeSourceContainer() { return AbstractEntitySourceImpl.this; } @Override public void addAttributeSource(AttributeSource attributeSource) { attributeSources.add( attributeSource ); } }, joinElement.getAttributes(), logicalTableName, NaturalIdMutability.NOT_NATURAL_ID ); } return secondaryTableSourcesMap; } protected JaxbHbmEntityBaseDefinition jaxbEntityMapping() { return jaxbEntityMapping; } @Override public Origin getOrigin() { return origin(); } @Override public EntityNamingSource getEntityNamingSource() { return entityNamingSource; } @Override public Boolean isAbstract() { return jaxbEntityMapping().isAbstract(); } @Override public boolean isLazy() { if ( jaxbEntityMapping.isLazy() == null ) { return metadataBuildingContext().getMappingDefaults().areEntitiesImplicitlyLazy(); } return jaxbEntityMapping().isLazy(); } @Override public String getProxy() { return jaxbEntityMapping.getProxy(); } @Override public int getBatchSize() { return jaxbEntityMapping.getBatchSize(); } @Override public boolean isDynamicInsert() { return jaxbEntityMapping.isDynamicInsert(); } @Override public boolean isDynamicUpdate() { return jaxbEntityMapping.isDynamicUpdate(); } @Override public boolean isSelectBeforeUpdate() { return jaxbEntityMapping.isSelectBeforeUpdate(); } protected EntityMode determineEntityMode() { return StringHelper.isNotEmpty( entityNamingSource.getClassName() ) ? EntityMode.POJO : EntityMode.MAP; } @Override public Map<EntityMode, String> getTuplizerClassMap() { return tuplizerClassMap; } @Override public String getCustomPersisterClassName() { return metadataBuildingContext().qualifyClassName( jaxbEntityMapping.getPersister() ); } @Override public String getCustomLoaderName() { return jaxbEntityMapping.getLoader() != null ? jaxbEntityMapping.getLoader().getQueryRef() : null; } @Override public CustomSql getCustomSqlInsert() { return Helper.buildCustomSql( jaxbEntityMapping.getSqlInsert() ); } @Override public CustomSql getCustomSqlUpdate() { return Helper.buildCustomSql( jaxbEntityMapping.getSqlUpdate() ); } @Override public CustomSql getCustomSqlDelete() { return Helper.buildCustomSql( jaxbEntityMapping.getSqlDelete() ); } @Override public String[] getSynchronizedTableNames() { if ( CollectionHelper.isEmpty( jaxbEntityMapping.getSynchronize() ) ) { return StringHelper.EMPTY_STRINGS; } else { final int size = jaxbEntityMapping.getSynchronize().size(); final String[] synchronizedTableNames = new String[size]; for ( int i = 0; i < size; i++ ) { synchronizedTableNames[i] = jaxbEntityMapping.getSynchronize().get( i ).getTable(); } return synchronizedTableNames; } } @Override public ToolingHintContext getToolingHintContext() { return toolingHintContext; } @Override public List<AttributeSource> attributeSources() { return attributeSources; } private EntityHierarchySourceImpl entityHierarchy; public void injectHierarchy(EntityHierarchySourceImpl entityHierarchy) { this.entityHierarchy = entityHierarchy; } @Override public EntityHierarchySource getHierarchy() { return entityHierarchy; } void add(SubclassEntitySource subclassEntitySource) { add( (SubclassEntitySourceImpl) subclassEntitySource ); } void add(SubclassEntitySourceImpl subclassEntitySource) { subclassEntitySource.injectHierarchy( entityHierarchy ); entityHierarchy.processSubclass( subclassEntitySource ); subclassEntitySources.add( subclassEntitySource ); } @Override public Map<String,SecondaryTableSource> getSecondaryTableMap() { return secondaryTableMap; } @Override public List<JpaCallbackSource> getJpaCallbackClasses() { return Collections.emptyList(); } @Override public List<JaxbHbmNamedQueryType> getNamedQueries() { return jaxbEntityMapping.getQuery(); } @Override public List<JaxbHbmNamedNativeQueryType> getNamedNativeQueries() { return jaxbEntityMapping.getSqlQuery(); } @Override public TruthValue quoteIdentifiersLocalToEntity() { // HBM does not allow for this return TruthValue.UNKNOWN; } }