AbstractEntityPersister.java

Index Score
org.hibernate.persister.entity
Hibernate

View: Reasons, Metrics, Source Code

These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.

MetricDescription
INTERFACE_COMPLEXITYInterface complexity
RETURNSNumber of return points from functions
PARAMSNumber of formal parameter declarations
EXITSProcedure exits
CYCLOMATICCyclomatic complexity
FUNCTIONSNumber of function declarations
LINE_COMMENTNumber of line comments
BLOCKSNumber of blocks
OPERATORSNumber of operators
PROGRAM_LENGTHHalstead program length
SIZESize of the file in bytes
ELOCEffective lines of code
LOCLines of code
COMPARISONSNumber of comparison operators
OPERANDSNumber of operands
EXEC_COMMENTSComments in executable code
LOOPSNumber of loops
LOGICAL_LINESNumber of statements
LINESNumber of lines in the source file
UNIQUE_OPERANDSNumber of unique operands
PROGRAM_VOCABHalstead program vocabulary
JAVA0145JAVA0145 Tab character used in source file
JAVA0126JAVA0126 Method declares unchecked exception in throws
WHITESPACENumber of whitespace lines
JAVA0117JAVA0117 Missing javadoc: method 'method'
JAVA0108JAVA0108 Incorrect javadoc: no @param tag for 'parameter'
DECL_COMMENTSComments in declarations
JAVA0173JAVA0173 Unused method parameter
COMMENTSComment lines
JAVA0259JAVA0259 Return of collection/array field
JAVA0115JAVA0115 Incorrect javadoc: no @throws or @exception tag for 'exception'
JAVA0144JAVA0144 Line exceeds maximum M characters
JAVA0110JAVA0110 Incorrect javadoc: no @return tag
JAVA0049JAVA0049 Nested block at depth N (maximum: M)
JAVA0177JAVA0177 Variable declaration missing initializer
JAVA0270JAVA0270 Use Java 5.0 enhanced for loop construct to iterate over all elements in an array
JAVA0254JAVA0254 Use enhanced for loop construct instead of Iterator
JAVA0138JAVA0138 N parameters defined for method (maximum: M)
JAVA0034JAVA0034 Missing braces in if statement
JAVA0179JAVA0179 Local variable hides visible field
DOC_COMMENTNumber of javadoc comment lines
JAVA0067JAVA0067 Array descriptor on identifier name
UNIQUE_OPERATORSNumber of unique operators
JAVA0082JAVA0082 Unnecessary widening cast
NEST_DEPTHMaximum nesting depth
PROGRAM_VOLUMEHalstead program volume
JAVA0136JAVA0136 N methods defined in class (maximum: M)
JAVA0288JAVA0288 Inconsistent null check
JAVA0100JAVA0100 Class contains N non-final fields (maximum: M)
JAVA0075JAVA0075 Method parameter hides field
/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Middleware LLC. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA * */ package org.hibernate.persister.entity; import java.io.Serializable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.Comparator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.hibernate.AssertionFailure; import org.hibernate.EntityMode; import org.hibernate.FetchMode; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.MappingException; import org.hibernate.QueryException; import org.hibernate.StaleObjectStateException; import org.hibernate.StaleStateException; import org.hibernate.jdbc.Expectation; import org.hibernate.jdbc.Expectations; import org.hibernate.jdbc.TooManyRowsAffectedException; import org.hibernate.dialect.lock.LockingStrategy; import org.hibernate.cache.CacheKey; import org.hibernate.cache.access.EntityRegionAccessStrategy; import org.hibernate.cache.entry.CacheEntry; import org.hibernate.cache.entry.CacheEntryStructure; import org.hibernate.cache.entry.StructuredCacheEntry; import org.hibernate.cache.entry.UnstructuredCacheEntry; import org.hibernate.engine.CascadeStyle; import org.hibernate.engine.CascadingAction; import org.hibernate.engine.EntityEntry; import org.hibernate.engine.Mapping; import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.engine.SessionImplementor; import org.hibernate.engine.Versioning; import org.hibernate.engine.ExecuteUpdateResultCheckStyle; import org.hibernate.engine.EntityKey; import org.hibernate.engine.ValueInclusion; import org.hibernate.engine.LoadQueryInfluencers; import org.hibernate.exception.JDBCExceptionHelper; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.PostInsertIdentifierGenerator; import org.hibernate.id.PostInsertIdentityPersister; import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; import org.hibernate.id.insert.Binder; import org.hibernate.intercept.LazyPropertyInitializer; import org.hibernate.intercept.FieldInterceptionHelper; import org.hibernate.intercept.FieldInterceptor; import org.hibernate.loader.entity.BatchingEntityLoader; import org.hibernate.loader.entity.CascadeEntityLoader; import org.hibernate.loader.entity.EntityLoader; import org.hibernate.loader.entity.UniqueEntityLoader; import org.hibernate.mapping.Column; import org.hibernate.mapping.Component; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.hibernate.mapping.Selectable; import org.hibernate.metadata.ClassMetadata; import org.hibernate.pretty.MessageHelper; import org.hibernate.property.BackrefPropertyAccessor; import org.hibernate.sql.Alias; import org.hibernate.sql.Delete; import org.hibernate.sql.Insert; import org.hibernate.sql.JoinFragment; import org.hibernate.sql.Select; import org.hibernate.sql.SelectFragment; import org.hibernate.sql.SimpleSelect; import org.hibernate.sql.Template; import org.hibernate.sql.Update; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.tuple.Tuplizer; import org.hibernate.type.AbstractComponentType; import org.hibernate.type.AssociationType; import org.hibernate.type.EntityType; import org.hibernate.type.Type; import org.hibernate.type.TypeFactory; import org.hibernate.type.VersionType; import org.hibernate.util.ArrayHelper; import org.hibernate.util.FilterHelper; import org.hibernate.util.StringHelper; /** * Basic functionality for persisting an entity via JDBC * through either generated or custom SQL * * @author Gavin King */ public abstract class AbstractEntityPersister implements OuterJoinLoadable, Queryable, ClassMetadata, UniqueKeyLoadable, SQLLoadable, LazyPropertyInitializer, PostInsertIdentityPersister, Lockable { private static final Logger log = LoggerFactory.getLogger( AbstractEntityPersister.class ); public static final String ENTITY_CLASS = "class"; // moved up from AbstractEntityPersister ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private final SessionFactoryImplementor factory; private final EntityRegionAccessStrategy cacheAccessStrategy; private final boolean isLazyPropertiesCacheable; private final CacheEntryStructure cacheEntryStructure; private final EntityMetamodel entityMetamodel; private final Map entityNameBySubclass = new HashMap(); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private final String[] rootTableKeyColumnNames; private final String[] identifierAliases; private final int identifierColumnSpan; private final String versionColumnName; private final boolean hasFormulaProperties; private final int batchSize; private final boolean hasSubselectLoadableCollections; protected final String rowIdName; private final Set lazyProperties; // The optional SQL string defined in the where attribute private final String sqlWhereString; private final String sqlWhereStringTemplate; //information about properties of this class, //including inherited properties //(only really needed for updatable/insertable properties) private final int[] propertyColumnSpans; private final String[] propertySubclassNames; private final String[][] propertyColumnAliases; private final String[][] propertyColumnNames; private final String[][] propertyColumnFormulaTemplates; private final boolean[][] propertyColumnUpdateable; private final boolean[][] propertyColumnInsertable; private final boolean[] propertyUniqueness; private final boolean[] propertySelectable; //information about lazy properties of this class private final String[] lazyPropertyNames; private final int[] lazyPropertyNumbers; private final Type[] lazyPropertyTypes; private final String[][] lazyPropertyColumnAliases; //information about all properties in class hierarchy private final String[] subclassPropertyNameClosure; private final String[] subclassPropertySubclassNameClosure; private final Type[] subclassPropertyTypeClosure; private final String[][] subclassPropertyFormulaTemplateClosure; private final String[][] subclassPropertyColumnNameClosure; private final FetchMode[] subclassPropertyFetchModeClosure; private final boolean[] subclassPropertyNullabilityClosure; private final boolean[] propertyDefinedOnSubclass; private final int[][] subclassPropertyColumnNumberClosure; private final int[][] subclassPropertyFormulaNumberClosure; private final CascadeStyle[] subclassPropertyCascadeStyleClosure; //information about all columns/formulas in class hierarchy private final String[] subclassColumnClosure; private final boolean[] subclassColumnLazyClosure; private final String[] subclassColumnAliasClosure; private final boolean[] subclassColumnSelectableClosure; private final String[] subclassFormulaClosure; private final String[] subclassFormulaTemplateClosure; private final String[] subclassFormulaAliasClosure; private final boolean[] subclassFormulaLazyClosure; // dynamic filters attached to the class-level private final FilterHelper filterHelper; private final Set affectingFetchProfileNames = new HashSet(); private final Map uniqueKeyLoaders = new HashMap(); private final Map lockers = new HashMap(); private final Map loaders = new HashMap(); // SQL strings private String sqlVersionSelectString; private String sqlSnapshotSelectString; private String sqlLazySelectString; private String sqlIdentityInsertString; private String sqlUpdateByRowIdString; private String sqlLazyUpdateByRowIdString; private String[] sqlDeleteStrings; private String[] sqlInsertStrings; private String[] sqlUpdateStrings; private String[] sqlLazyUpdateStrings; private String sqlInsertGeneratedValuesSelectString; private String sqlUpdateGeneratedValuesSelectString; //Custom SQL (would be better if these were private) protected boolean[] insertCallable; protected boolean[] updateCallable; protected boolean[] deleteCallable; protected String[] customSQLInsert; protected String[] customSQLUpdate; protected String[] customSQLDelete; protected ExecuteUpdateResultCheckStyle[] insertResultCheckStyles; protected ExecuteUpdateResultCheckStyle[] updateResultCheckStyles; protected ExecuteUpdateResultCheckStyle[] deleteResultCheckStyles; private InsertGeneratedIdentifierDelegate identityDelegate; private boolean[] tableHasColumns; private final String loaderName; private UniqueEntityLoader queryLoader; private final String temporaryIdTableName; private final String temporaryIdTableDDL; private final Map subclassPropertyAliases = new HashMap(); private final Map subclassPropertyColumnNames = new HashMap(); protected final BasicEntityPropertyMapping propertyMapping; protected void addDiscriminatorToInsert(Insert insert) {} protected void addDiscriminatorToSelect(SelectFragment select, String name, String suffix) {} protected abstract int[] getSubclassColumnTableNumberClosure(); protected abstract int[] getSubclassFormulaTableNumberClosure(); public abstract String getSubclassTableName(int j); protected abstract String[] getSubclassTableKeyColumns(int j); protected abstract boolean isClassOrSuperclassTable(int j); protected abstract int getSubclassTableSpan(); protected abstract int getTableSpan(); protected abstract boolean isTableCascadeDeleteEnabled(int j); protected abstract String getTableName(int j); protected abstract String[] getKeyColumns(int j); protected abstract boolean isPropertyOfTable(int property, int j); protected abstract int[] getPropertyTableNumbersInSelect(); protected abstract int[] getPropertyTableNumbers(); protected abstract int getSubclassPropertyTableNumber(int i); protected abstract String filterFragment(String alias) throws MappingException; private static final String DISCRIMINATOR_ALIAS = "clazz_"; public String getDiscriminatorColumnName() { return DISCRIMINATOR_ALIAS; } protected String getDiscriminatorAlias() { return DISCRIMINATOR_ALIAS; } protected String getDiscriminatorFormulaTemplate() { return null; } protected boolean isInverseTable(int j) { return false; } protected boolean isNullableTable(int j) { return false; } protected boolean isNullableSubclassTable(int j) { return false; } protected boolean isInverseSubclassTable(int j) { return false; } public boolean isSubclassEntityName(String entityName) { return entityMetamodel.getSubclassEntityNames().contains(entityName); } private boolean[] getTableHasColumns() { return tableHasColumns; } public String[] getRootTableKeyColumnNames() { return rootTableKeyColumnNames; } protected String[] getSQLUpdateByRowIdStrings() { if ( sqlUpdateByRowIdString == null ) { throw new AssertionFailure( "no update by row id" ); } String[] result = new String[getTableSpan() + 1]; result[0] = sqlUpdateByRowIdString; System.arraycopy( sqlUpdateStrings, 0, result, 1, getTableSpan() ); return result; } protected String[] getSQLLazyUpdateByRowIdStrings() { if ( sqlLazyUpdateByRowIdString == null ) { throw new AssertionFailure( "no update by row id" ); } String[] result = new String[getTableSpan()]; result[0] = sqlLazyUpdateByRowIdString; for ( int i = 1; i < getTableSpan(); i++ ) { result[i] = sqlLazyUpdateStrings[i]; } return result; } protected String getSQLSnapshotSelectString() { return sqlSnapshotSelectString; } protected String getSQLLazySelectString() { return sqlLazySelectString; } protected String[] getSQLDeleteStrings() { return sqlDeleteStrings; } protected String[] getSQLInsertStrings() { return sqlInsertStrings; } protected String[] getSQLUpdateStrings() { return sqlUpdateStrings; } protected String[] getSQLLazyUpdateStrings() { return sqlLazyUpdateStrings; } /** * The query that inserts a row, letting the database generate an id * * @return The IDENTITY-based insertion query. */ protected String getSQLIdentityInsertString() { return sqlIdentityInsertString; } protected String getVersionSelectString() { return sqlVersionSelectString; } protected boolean isInsertCallable(int j) { return insertCallable[j]; } protected boolean isUpdateCallable(int j) { return updateCallable[j]; } protected boolean isDeleteCallable(int j) { return deleteCallable[j]; } protected boolean isSubclassPropertyDeferred(String propertyName, String entityName) { return false; } protected boolean isSubclassTableSequentialSelect(int j) { return false; } public boolean hasSequentialSelect() { return false; } /** * Decide which tables need to be updated. * <p/> * The return here is an array of boolean values with each index corresponding * to a given table in the scope of this persister. * * @param dirtyProperties The indices of all the entity properties considered dirty. * @param hasDirtyCollection Whether any collections owned by the entity which were considered dirty. * * @return Array of booleans indicating which table require updating. */ protected boolean[] getTableUpdateNeeded(final int[] dirtyProperties, boolean hasDirtyCollection) { if ( dirtyProperties == null ) { return getTableHasColumns(); // for objects that came in via update() } else { boolean[] updateability = getPropertyUpdateability(); int[] propertyTableNumbers = getPropertyTableNumbers(); boolean[] tableUpdateNeeded = new boolean[ getTableSpan() ]; for ( int i = 0; i < dirtyProperties.length; i++ ) { int property = dirtyProperties[i]; int table = propertyTableNumbers[property]; tableUpdateNeeded[table] = tableUpdateNeeded[table] || ( getPropertyColumnSpan(property) > 0 && updateability[property] ); } if ( isVersioned() ) { tableUpdateNeeded[0] = tableUpdateNeeded[0] || Versioning.isVersionIncrementRequired( dirtyProperties, hasDirtyCollection, getPropertyVersionability() ); } return tableUpdateNeeded; } } public boolean hasRowId() { return rowIdName != null; } public AbstractEntityPersister( final PersistentClass persistentClass, final EntityRegionAccessStrategy cacheAccessStrategy, final SessionFactoryImplementor factory) throws HibernateException { // moved up from AbstractEntityPersister ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ this.factory = factory; this.cacheAccessStrategy = cacheAccessStrategy; isLazyPropertiesCacheable = persistentClass.isLazyPropertiesCacheable(); this.cacheEntryStructure = factory.getSettings().isStructuredCacheEntriesEnabled() ? (CacheEntryStructure) new StructuredCacheEntry(this) : (CacheEntryStructure) new UnstructuredCacheEntry(); this.entityMetamodel = new EntityMetamodel( persistentClass, factory ); if ( persistentClass.hasPojoRepresentation() ) { //TODO: this is currently specific to pojos, but need to be available for all entity-modes Iterator iter = persistentClass.getSubclassIterator(); while ( iter.hasNext() ) { PersistentClass pc = ( PersistentClass ) iter.next(); entityNameBySubclass.put( pc.getMappedClass(), pc.getEntityName() ); } } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ int batch = persistentClass.getBatchSize(); if ( batch == -1 ) { batch = factory.getSettings().getDefaultBatchFetchSize(); } batchSize = batch; hasSubselectLoadableCollections = persistentClass.hasSubselectLoadableCollections(); propertyMapping = new BasicEntityPropertyMapping( this ); // IDENTIFIER identifierColumnSpan = persistentClass.getIdentifier().getColumnSpan(); rootTableKeyColumnNames = new String[identifierColumnSpan]; identifierAliases = new String[identifierColumnSpan]; rowIdName = persistentClass.getRootTable().getRowId(); loaderName = persistentClass.getLoaderName(); Iterator iter = persistentClass.getIdentifier().getColumnIterator(); int i = 0; while ( iter.hasNext() ) { Column col = ( Column ) iter.next(); rootTableKeyColumnNames[i] = col.getQuotedName( factory.getDialect() ); identifierAliases[i] = col.getAlias( factory.getDialect(), persistentClass.getRootTable() ); i++; } // VERSION if ( persistentClass.isVersioned() ) { versionColumnName = ( ( Column ) persistentClass.getVersion().getColumnIterator().next() ).getQuotedName( factory.getDialect() ); } else { versionColumnName = null; } //WHERE STRING sqlWhereString = StringHelper.isNotEmpty( persistentClass.getWhere() ) ? "( " + persistentClass.getWhere() + ") " : null; sqlWhereStringTemplate = sqlWhereString == null ? null : Template.renderWhereStringTemplate( sqlWhereString, factory.getDialect(), factory.getSqlFunctionRegistry() ); // PROPERTIES final boolean lazyAvailable = isInstrumented(EntityMode.POJO); int hydrateSpan = entityMetamodel.getPropertySpan(); propertyColumnSpans = new int[hydrateSpan]; propertySubclassNames = new String[hydrateSpan]; propertyColumnAliases = new String[hydrateSpan][]; propertyColumnNames = new String[hydrateSpan][]; propertyColumnFormulaTemplates = new String[hydrateSpan][]; propertyUniqueness = new boolean[hydrateSpan]; propertySelectable = new boolean[hydrateSpan]; propertyColumnUpdateable = new boolean[hydrateSpan][]; propertyColumnInsertable = new boolean[hydrateSpan][]; HashSet thisClassProperties = new HashSet(); lazyProperties = new HashSet(); ArrayList lazyNames = new ArrayList(); ArrayList lazyNumbers = new ArrayList(); ArrayList lazyTypes = new ArrayList(); ArrayList lazyColAliases = new ArrayList(); iter = persistentClass.getPropertyClosureIterator(); i = 0; boolean foundFormula = false; while ( iter.hasNext() ) { Property prop = ( Property ) iter.next(); thisClassProperties.add( prop ); int span = prop.getColumnSpan(); propertyColumnSpans[i] = span; propertySubclassNames[i] = prop.getPersistentClass().getEntityName(); String[] colNames = new String[span]; String[] colAliases = new String[span]; String[] templates = new String[span]; Iterator colIter = prop.getColumnIterator(); int k = 0; while ( colIter.hasNext() ) { Selectable thing = ( Selectable ) colIter.next(); colAliases[k] = thing.getAlias( factory.getDialect() , prop.getValue().getTable() ); if ( thing.isFormula() ) { foundFormula = true; templates[k] = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() ); } else { colNames[k] = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() ); } k++; } propertyColumnNames[i] = colNames; propertyColumnFormulaTemplates[i] = templates; propertyColumnAliases[i] = colAliases; if ( lazyAvailable && prop.isLazy() ) { lazyProperties.add( prop.getName() ); lazyNames.add( prop.getName() ); lazyNumbers.add( new Integer( i ) ); lazyTypes.add( prop.getValue().getType() ); lazyColAliases.add( colAliases ); } propertyColumnUpdateable[i] = prop.getValue().getColumnUpdateability(); propertyColumnInsertable[i] = prop.getValue().getColumnInsertability(); propertySelectable[i] = prop.isSelectable(); propertyUniqueness[i] = prop.getValue().isAlternateUniqueKey(); i++; } hasFormulaProperties = foundFormula; lazyPropertyColumnAliases = ArrayHelper.to2DStringArray( lazyColAliases ); lazyPropertyNames = ArrayHelper.toStringArray( lazyNames ); lazyPropertyNumbers = ArrayHelper.toIntArray( lazyNumbers ); lazyPropertyTypes = ArrayHelper.toTypeArray( lazyTypes ); // SUBCLASS PROPERTY CLOSURE ArrayList columns = new ArrayList(); ArrayList columnsLazy = new ArrayList(); ArrayList aliases = new ArrayList(); ArrayList formulas = new ArrayList(); ArrayList formulaAliases = new ArrayList(); ArrayList formulaTemplates = new ArrayList(); ArrayList formulasLazy = new ArrayList(); ArrayList types = new ArrayList(); ArrayList names = new ArrayList(); ArrayList classes = new ArrayList(); ArrayList templates = new ArrayList(); ArrayList propColumns = new ArrayList(); ArrayList joinedFetchesList = new ArrayList(); ArrayList cascades = new ArrayList(); ArrayList definedBySubclass = new ArrayList(); ArrayList propColumnNumbers = new ArrayList(); ArrayList propFormulaNumbers = new ArrayList(); ArrayList columnSelectables = new ArrayList(); ArrayList propNullables = new ArrayList(); iter = persistentClass.getSubclassPropertyClosureIterator(); while ( iter.hasNext() ) { Property prop = ( Property ) iter.next(); names.add( prop.getName() ); classes.add( prop.getPersistentClass().getEntityName() ); boolean isDefinedBySubclass = !thisClassProperties.contains( prop ); definedBySubclass.add( Boolean.valueOf( isDefinedBySubclass ) ); propNullables.add( Boolean.valueOf( prop.isOptional() || isDefinedBySubclass ) ); //TODO: is this completely correct? types.add( prop.getType() ); Iterator colIter = prop.getColumnIterator(); String[] cols = new String[prop.getColumnSpan()]; String[] forms = new String[prop.getColumnSpan()]; int[] colnos = new int[prop.getColumnSpan()]; int[] formnos = new int[prop.getColumnSpan()]; int l = 0; Boolean lazy = Boolean.valueOf( prop.isLazy() && lazyAvailable ); while ( colIter.hasNext() ) { Selectable thing = ( Selectable ) colIter.next(); if ( thing.isFormula() ) { String template = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() ); formnos[l] = formulaTemplates.size(); colnos[l] = -1; formulaTemplates.add( template ); forms[l] = template; formulas.add( thing.getText( factory.getDialect() ) ); formulaAliases.add( thing.getAlias( factory.getDialect() ) ); formulasLazy.add( lazy ); } else { String colName = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() ); colnos[l] = columns.size(); //before add :-) formnos[l] = -1; columns.add( colName ); cols[l] = colName; aliases.add( thing.getAlias( factory.getDialect(), prop.getValue().getTable() ) ); columnsLazy.add( lazy ); columnSelectables.add( Boolean.valueOf( prop.isSelectable() ) ); } l++; } propColumns.add( cols ); templates.add( forms ); propColumnNumbers.add( colnos ); propFormulaNumbers.add( formnos ); joinedFetchesList.add( prop.getValue().getFetchMode() ); cascades.add( prop.getCascadeStyle() ); } subclassColumnClosure = ArrayHelper.toStringArray( columns ); subclassColumnAliasClosure = ArrayHelper.toStringArray( aliases ); subclassColumnLazyClosure = ArrayHelper.toBooleanArray( columnsLazy ); subclassColumnSelectableClosure = ArrayHelper.toBooleanArray( columnSelectables ); subclassFormulaClosure = ArrayHelper.toStringArray( formulas ); subclassFormulaTemplateClosure = ArrayHelper.toStringArray( formulaTemplates ); subclassFormulaAliasClosure = ArrayHelper.toStringArray( formulaAliases ); subclassFormulaLazyClosure = ArrayHelper.toBooleanArray( formulasLazy ); subclassPropertyNameClosure = ArrayHelper.toStringArray( names ); subclassPropertySubclassNameClosure = ArrayHelper.toStringArray( classes ); subclassPropertyTypeClosure = ArrayHelper.toTypeArray( types ); subclassPropertyNullabilityClosure = ArrayHelper.toBooleanArray( propNullables ); subclassPropertyFormulaTemplateClosure = ArrayHelper.to2DStringArray( templates ); subclassPropertyColumnNameClosure = ArrayHelper.to2DStringArray( propColumns ); subclassPropertyColumnNumberClosure = ArrayHelper.to2DIntArray( propColumnNumbers ); subclassPropertyFormulaNumberClosure = ArrayHelper.to2DIntArray( propFormulaNumbers ); subclassPropertyCascadeStyleClosure = new CascadeStyle[cascades.size()]; iter = cascades.iterator(); int j = 0; while ( iter.hasNext() ) { subclassPropertyCascadeStyleClosure[j++] = ( CascadeStyle ) iter.next(); } subclassPropertyFetchModeClosure = new FetchMode[joinedFetchesList.size()]; iter = joinedFetchesList.iterator(); j = 0; while ( iter.hasNext() ) { subclassPropertyFetchModeClosure[j++] = ( FetchMode ) iter.next(); } propertyDefinedOnSubclass = new boolean[definedBySubclass.size()]; iter = definedBySubclass.iterator(); j = 0; while ( iter.hasNext() ) { propertyDefinedOnSubclass[j++] = ( ( Boolean ) iter.next() ).booleanValue(); } // Handle any filters applied to the class level filterHelper = new FilterHelper( persistentClass.getFilterMap(), factory.getDialect(), factory.getSqlFunctionRegistry() ); temporaryIdTableName = persistentClass.getTemporaryIdTableName(); temporaryIdTableDDL = persistentClass.getTemporaryIdTableDDL(); } protected String generateLazySelectString() { if ( !entityMetamodel.hasLazyProperties() ) { return null; } HashSet tableNumbers = new HashSet(); ArrayList columnNumbers = new ArrayList(); ArrayList formulaNumbers = new ArrayList(); for ( int i = 0; i < lazyPropertyNames.length; i++ ) { // all this only really needs to consider properties // of this class, not its subclasses, but since we // are reusing code used for sequential selects, we // use the subclass closure int propertyNumber = getSubclassPropertyIndex( lazyPropertyNames[i] ); int tableNumber = getSubclassPropertyTableNumber( propertyNumber ); tableNumbers.add( new Integer( tableNumber ) ); int[] colNumbers = subclassPropertyColumnNumberClosure[propertyNumber]; for ( int j = 0; j < colNumbers.length; j++ ) { if ( colNumbers[j]!=-1 ) { columnNumbers.add( new Integer( colNumbers[j] ) ); } } int[] formNumbers = subclassPropertyFormulaNumberClosure[propertyNumber]; for ( int j = 0; j < formNumbers.length; j++ ) { if ( formNumbers[j]!=-1 ) { formulaNumbers.add( new Integer( formNumbers[j] ) ); } } } if ( columnNumbers.size()==0 && formulaNumbers.size()==0 ) { // only one-to-one is lazy fetched return null; } return renderSelect( ArrayHelper.toIntArray( tableNumbers ), ArrayHelper.toIntArray( columnNumbers ), ArrayHelper.toIntArray( formulaNumbers ) ); } public Object initializeLazyProperty(String fieldName, Object entity, SessionImplementor session) throws HibernateException { final Serializable id = session.getContextEntityIdentifier( entity ); final EntityEntry entry = session.getPersistenceContext().getEntry( entity ); if ( entry == null ) { throw new HibernateException( "entity is not associated with the session: " + id ); } if ( log.isTraceEnabled() ) { log.trace( "initializing lazy properties of: " + MessageHelper.infoString( this, id, getFactory() ) + ", field access: " + fieldName ); } if ( hasCache() ) { CacheKey cacheKey = new CacheKey(id, getIdentifierType(), getEntityName(), session.getEntityMode(), getFactory() ); Object ce = getCacheAccessStrategy().get( cacheKey, session.getTimestamp() ); if (ce!=null) { CacheEntry cacheEntry = (CacheEntry) getCacheEntryStructure().destructure(ce, factory); if ( !cacheEntry.areLazyPropertiesUnfetched() ) { //note early exit here: return initializeLazyPropertiesFromCache( fieldName, entity, session, entry, cacheEntry ); } } } return initializeLazyPropertiesFromDatastore( fieldName, entity, session, id, entry ); } private Object initializeLazyPropertiesFromDatastore( final String fieldName, final Object entity, final SessionImplementor session, final Serializable id, final EntityEntry entry) { if ( !hasLazyProperties() ) { throw new AssertionFailure("no lazy properties"); } log.trace("initializing lazy properties from datastore"); try { Object result = null; PreparedStatement ps = null; try { final String lazySelect = getSQLLazySelectString(); ResultSet rs = null; try { if ( lazySelect != null ) { // null sql means that the only lazy properties // are shared PK one-to-one associations which are // handled differently in the Type#nullSafeGet code... ps = session.getBatcher().prepareSelectStatement(lazySelect); getIdentifierType().nullSafeSet( ps, id, 1, session ); rs = ps.executeQuery(); rs.next(); } final Object[] snapshot = entry.getLoadedState(); for ( int j = 0; j < lazyPropertyNames.length; j++ ) { Object propValue = lazyPropertyTypes[j].nullSafeGet( rs, lazyPropertyColumnAliases[j], session, entity ); if ( initializeLazyProperty( fieldName, entity, session, snapshot, j, propValue ) ) { result = propValue; } } } finally { if ( rs != null ) { rs.close(); } } } finally { if ( ps != null ) { session.getBatcher().closeStatement( ps ); } } log.trace( "done initializing lazy properties" ); return result; } catch ( SQLException sqle ) { throw JDBCExceptionHelper.convert( getFactory().getSQLExceptionConverter(), sqle, "could not initialize lazy properties: " + MessageHelper.infoString( this, id, getFactory() ), getSQLLazySelectString() ); } } private Object initializeLazyPropertiesFromCache( final String fieldName, final Object entity, final SessionImplementor session, final EntityEntry entry, final CacheEntry cacheEntry ) { log.trace("initializing lazy properties from second-level cache"); Object result = null; Serializable[] disassembledValues = cacheEntry.getDisassembledState(); final Object[] snapshot = entry.getLoadedState(); for ( int j = 0; j < lazyPropertyNames.length; j++ ) { final Object propValue = lazyPropertyTypes[j].assemble( disassembledValues[ lazyPropertyNumbers[j] ], session, entity ); if ( initializeLazyProperty( fieldName, entity, session, snapshot, j, propValue ) ) { result = propValue; } } log.trace( "done initializing lazy properties" ); return result; } private boolean initializeLazyProperty( final String fieldName, final Object entity, final SessionImplementor session, final Object[] snapshot, final int j, final Object propValue) { setPropertyValue( entity, lazyPropertyNumbers[j], propValue, session.getEntityMode() ); if (snapshot != null) { // object have been loaded with setReadOnly(true); HHH-2236 snapshot[ lazyPropertyNumbers[j] ] = lazyPropertyTypes[j].deepCopy( propValue, session.getEntityMode(), factory ); } return fieldName.equals( lazyPropertyNames[j] ); } public boolean isBatchable() { return optimisticLockMode()==Versioning.OPTIMISTIC_LOCK_NONE || ( !isVersioned() && optimisticLockMode()==Versioning.OPTIMISTIC_LOCK_VERSION ) || getFactory().getSettings().isJdbcBatchVersionedData(); } public Serializable[] getQuerySpaces() { return getPropertySpaces(); } protected Set getLazyProperties() { return lazyProperties; } public boolean isBatchLoadable() { return batchSize > 1; } public String[] getIdentifierColumnNames() { return rootTableKeyColumnNames; } protected int getIdentifierColumnSpan() { return identifierColumnSpan; } protected String[] getIdentifierAliases() { return identifierAliases; } public String getVersionColumnName() { return versionColumnName; } protected String getVersionedTableName() { return getTableName( 0 ); } protected boolean[] getSubclassColumnLazyiness() { return subclassColumnLazyClosure; } protected boolean[] getSubclassFormulaLazyiness() { return subclassFormulaLazyClosure; } /** * We can't immediately add to the cache if we have formulas * which must be evaluated, or if we have the possibility of * two concurrent updates to the same item being merged on * the database. This can happen if (a) the item is not * versioned and either (b) we have dynamic update enabled * or (c) we have multiple tables holding the state of the * item. */ public boolean isCacheInvalidationRequired() { return hasFormulaProperties() || ( !isVersioned() && ( entityMetamodel.isDynamicUpdate() || getTableSpan() > 1 ) ); } public boolean isLazyPropertiesCacheable() { return isLazyPropertiesCacheable; } public String selectFragment(String alias, String suffix) { return identifierSelectFragment( alias, suffix ) + propertySelectFragment( alias, suffix, false ); } public String[] getIdentifierAliases(String suffix) { // NOTE: this assumes something about how propertySelectFragment is implemented by the subclass! // was toUnqotedAliasStrings( getIdentiferColumnNames() ) before - now tried // to remove that unqoting and missing aliases.. return new Alias( suffix ).toAliasStrings( getIdentifierAliases() ); } public String[] getPropertyAliases(String suffix, int i) { // NOTE: this assumes something about how propertySelectFragment is implemented by the subclass! return new Alias( suffix ).toUnquotedAliasStrings( propertyColumnAliases[i] ); } public String getDiscriminatorAlias(String suffix) { // NOTE: this assumes something about how propertySelectFragment is implemented by the subclass! // was toUnqotedAliasStrings( getdiscriminatorColumnName() ) before - now tried // to remove that unqoting and missing aliases.. return entityMetamodel.hasSubclasses() ? new Alias( suffix ).toAliasString( getDiscriminatorAlias() ) : null; } public String identifierSelectFragment(String name, String suffix) { return new SelectFragment() .setSuffix( suffix ) .addColumns( name, getIdentifierColumnNames(), getIdentifierAliases() ) .toFragmentString() .substring( 2 ); //strip leading ", " } public String propertySelectFragment(String name, String suffix, boolean allProperties) { SelectFragment select = new SelectFragment() .setSuffix( suffix ) .setUsedAliases( getIdentifierAliases() ); int[] columnTableNumbers = getSubclassColumnTableNumberClosure(); String[] columnAliases = getSubclassColumnAliasClosure(); String[] columns = getSubclassColumnClosure(); for ( int i = 0; i < getSubclassColumnClosure().length; i++ ) { boolean selectable = ( allProperties || !subclassColumnLazyClosure[i] ) && !isSubclassTableSequentialSelect( columnTableNumbers[i] ) && subclassColumnSelectableClosure[i]; if ( selectable ) { String subalias = generateTableAlias( name, columnTableNumbers[i] ); select.addColumn( subalias, columns[i], columnAliases[i] ); } } int[] formulaTableNumbers = getSubclassFormulaTableNumberClosure(); String[] formulaTemplates = getSubclassFormulaTemplateClosure(); String[] formulaAliases = getSubclassFormulaAliasClosure(); for ( int i = 0; i < getSubclassFormulaTemplateClosure().length; i++ ) { boolean selectable = ( allProperties || !subclassFormulaLazyClosure[i] ) && !isSubclassTableSequentialSelect( formulaTableNumbers[i] ); if ( selectable ) { String subalias = generateTableAlias( name, formulaTableNumbers[i] ); select.addFormula( subalias, formulaTemplates[i], formulaAliases[i] ); } } if ( entityMetamodel.hasSubclasses() ) { addDiscriminatorToSelect( select, name, suffix ); } if ( hasRowId() ) { select.addColumn( name, rowIdName, ROWID_ALIAS ); } return select.toFragmentString(); } public Object[] getDatabaseSnapshot(Serializable id, SessionImplementor session) throws HibernateException { if ( log.isTraceEnabled() ) { log.trace( "Getting current persistent state for: " + MessageHelper.infoString( this, id, getFactory() ) ); } try { PreparedStatement ps = session.getBatcher().prepareSelectStatement( getSQLSnapshotSelectString() ); try { getIdentifierType().nullSafeSet( ps, id, 1, session ); //if ( isVersioned() ) getVersionType().nullSafeSet( ps, version, getIdentifierColumnSpan()+1, session ); ResultSet rs = ps.executeQuery(); try { //if there is no resulting row, return null if ( !rs.next() ) { return null; } //otherwise return the "hydrated" state (ie. associations are not resolved) Type[] types = getPropertyTypes(); Object[] values = new Object[types.length]; boolean[] includeProperty = getPropertyUpdateability(); for ( int i = 0; i < types.length; i++ ) { if ( includeProperty[i] ) { values[i] = types[i].hydrate( rs, getPropertyAliases( "", i ), session, null ); //null owner ok?? } } return values; } finally { rs.close(); } } finally { session.getBatcher().closeStatement( ps ); } } catch ( SQLException sqle ) { throw JDBCExceptionHelper.convert( getFactory().getSQLExceptionConverter(), sqle, "could not retrieve snapshot: " + MessageHelper.infoString( this, id, getFactory() ), getSQLSnapshotSelectString() ); } } /** * Generate the SQL that selects the version number by id */ protected String generateSelectVersionString() { SimpleSelect select = new SimpleSelect( getFactory().getDialect() ) .setTableName( getVersionedTableName() ); if ( isVersioned() ) { select.addColumn( versionColumnName ); } else { select.addColumns( rootTableKeyColumnNames ); } if ( getFactory().getSettings().isCommentsEnabled() ) { select.setComment( "get version " + getEntityName() ); } return select.addCondition( rootTableKeyColumnNames, "=?" ).toStatementString(); } protected String generateInsertGeneratedValuesSelectString() { return generateGeneratedValuesSelectString( getPropertyInsertGenerationInclusions() ); } protected String generateUpdateGeneratedValuesSelectString() { return generateGeneratedValuesSelectString( getPropertyUpdateGenerationInclusions() ); } private String generateGeneratedValuesSelectString(ValueInclusion[] inclusions) { Select select = new Select( getFactory().getDialect() ); if ( getFactory().getSettings().isCommentsEnabled() ) { select.setComment( "get generated state " + getEntityName() ); } String[] aliasedIdColumns = StringHelper.qualify( getRootAlias(), getIdentifierColumnNames() ); // Here we render the select column list based on the properties defined as being generated. // For partial component generation, we currently just re-select the whole component // rather than trying to handle the individual generated portions. String selectClause = concretePropertySelectFragment( getRootAlias(), inclusions ); selectClause = selectClause.substring( 2 ); String fromClause = fromTableFragment( getRootAlias() ) + fromJoinFragment( getRootAlias(), true, false ); String whereClause = new StringBuffer() .append( StringHelper.join( "=? and ", aliasedIdColumns ) ) .append( "=?" ) .append( whereJoinFragment( getRootAlias(), true, false ) ) .toString(); return select.setSelectClause( selectClause ) .setFromClause( fromClause ) .setOuterJoins( "", "" ) .setWhereClause( whereClause ) .toStatementString(); } protected static interface InclusionChecker { public boolean includeProperty(int propertyNumber); } protected String concretePropertySelectFragment(String alias, final ValueInclusion[] inclusions) { return concretePropertySelectFragment( alias, new InclusionChecker() { // TODO : currently we really do not handle ValueInclusion.PARTIAL... // ValueInclusion.PARTIAL would indicate parts of a component need to // be included in the select; currently we then just render the entire // component into the select clause in that case. public boolean includeProperty(int propertyNumber) { return inclusions[propertyNumber] != ValueInclusion.NONE; } } ); } protected String concretePropertySelectFragment(String alias, final boolean[] includeProperty) { return concretePropertySelectFragment( alias, new InclusionChecker() { public boolean includeProperty(int propertyNumber) { return includeProperty[propertyNumber]; } } ); } protected String concretePropertySelectFragment(String alias, InclusionChecker inclusionChecker) { int propertyCount = getPropertyNames().length; int[] propertyTableNumbers = getPropertyTableNumbersInSelect(); SelectFragment frag = new SelectFragment(); for ( int i = 0; i < propertyCount; i++ ) { if ( inclusionChecker.includeProperty( i ) ) { frag.addColumns( generateTableAlias( alias, propertyTableNumbers[i] ), propertyColumnNames[i], propertyColumnAliases[i] ); frag.addFormulas( generateTableAlias( alias, propertyTableNumbers[i] ), propertyColumnFormulaTemplates[i], propertyColumnAliases[i] ); } } return frag.toFragmentString(); } protected String generateSnapshotSelectString() { //TODO: should we use SELECT .. FOR UPDATE? Select select = new Select( getFactory().getDialect() ); if ( getFactory().getSettings().isCommentsEnabled() ) { select.setComment( "get current state " + getEntityName() ); } String[] aliasedIdColumns = StringHelper.qualify( getRootAlias(), getIdentifierColumnNames() ); String selectClause = StringHelper.join( ", ", aliasedIdColumns ) + concretePropertySelectFragment( getRootAlias(), getPropertyUpdateability() ); String fromClause = fromTableFragment( getRootAlias() ) + fromJoinFragment( getRootAlias(), true, false ); String whereClause = new StringBuffer() .append( StringHelper.join( "=? and ", aliasedIdColumns ) ) .append( "=?" ) .append( whereJoinFragment( getRootAlias(), true, false ) ) .toString(); /*if ( isVersioned() ) { where.append(" and ") .append( getVersionColumnName() ) .append("=?"); }*/ return select.setSelectClause( selectClause ) .setFromClause( fromClause ) .setOuterJoins( "", "" ) .setWhereClause( whereClause ) .toStatementString(); } public Object forceVersionIncrement(Serializable id, Object currentVersion, SessionImplementor session) { if ( !isVersioned() ) { throw new AssertionFailure( "cannot force version increment on non-versioned entity" ); } if ( isVersionPropertyGenerated() ) { // the difficulty here is exactly what do we update in order to // force the version to be incremented in the db... throw new HibernateException( "LockMode.FORCE is currently not supported for generated version properties" ); } Object nextVersion = getVersionType().next( currentVersion, session ); if ( log.isTraceEnabled() ) { log.trace( "Forcing version increment [" + MessageHelper.infoString( this, id, getFactory() ) + "; " + getVersionType().toLoggableString( currentVersion, getFactory() ) + " -> " + getVersionType().toLoggableString( nextVersion, getFactory() ) + "]" ); } // todo : cache this sql... String versionIncrementString = generateVersionIncrementUpdateString(); PreparedStatement st = null; try { try { st = session.getBatcher().prepareStatement( versionIncrementString ); getVersionType().nullSafeSet( st, nextVersion, 1, session ); getIdentifierType().nullSafeSet( st, id, 2, session ); getVersionType().nullSafeSet( st, currentVersion, 2 + getIdentifierColumnSpan(), session ); int rows = st.executeUpdate(); if ( rows != 1 ) { throw new StaleObjectStateException( getEntityName(), id ); } } finally { session.getBatcher().closeStatement( st ); } } catch ( SQLException sqle ) { throw JDBCExceptionHelper.convert( getFactory().getSQLExceptionConverter(), sqle, "could not retrieve version: " + MessageHelper.infoString( this, id, getFactory() ), getVersionSelectString() ); } return nextVersion; } private String generateVersionIncrementUpdateString() { Update update = new Update( getFactory().getDialect() ); update.setTableName( getTableName( 0 ) ); if ( getFactory().getSettings().isCommentsEnabled() ) { update.setComment( "forced version increment" ); } update.addColumn( getVersionColumnName() ); update.setPrimaryKeyColumnNames( getIdentifierColumnNames() ); update.setVersionColumnName( getVersionColumnName() ); return update.toStatementString(); } /** * Retrieve the version number */ public Object getCurrentVersion(Serializable id, SessionImplementor session) throws HibernateException { if ( log.isTraceEnabled() ) { log.trace( "Getting version: " + MessageHelper.infoString( this, id, getFactory() ) ); } try { PreparedStatement st = session.getBatcher().prepareSelectStatement( getVersionSelectString() ); try { getIdentifierType().nullSafeSet( st, id, 1, session ); ResultSet rs = st.executeQuery(); try { if ( !rs.next() ) { return null; } if ( !isVersioned() ) { return this; } return getVersionType().nullSafeGet( rs, getVersionColumnName(), session, null ); } finally { rs.close(); } } finally { session.getBatcher().closeStatement( st ); } } catch ( SQLException sqle ) { throw JDBCExceptionHelper.convert( getFactory().getSQLExceptionConverter(), sqle, "could not retrieve version: " + MessageHelper.infoString( this, id, getFactory() ), getVersionSelectString() ); } } protected void initLockers() { lockers.put( LockMode.READ, generateLocker( LockMode.READ ) ); lockers.put( LockMode.UPGRADE, generateLocker( LockMode.UPGRADE ) ); lockers.put( LockMode.UPGRADE_NOWAIT, generateLocker( LockMode.UPGRADE_NOWAIT ) ); lockers.put( LockMode.FORCE, generateLocker( LockMode.FORCE ) ); } protected LockingStrategy generateLocker(LockMode lockMode) { return factory.getDialect().getLockingStrategy( this, lockMode ); } private LockingStrategy getLocker(LockMode lockMode) { return ( LockingStrategy ) lockers.get( lockMode ); } public void lock( Serializable id, Object version, Object object, LockMode lockMode, SessionImplementor session) throws HibernateException { getLocker( lockMode ).lock( id, version, object, session ); } public String getRootTableName() { return getSubclassTableName( 0 ); } public String getRootTableAlias(String drivingAlias) { return drivingAlias; } public String[] getRootTableIdentifierColumnNames() { return getRootTableKeyColumnNames(); } public String[] toColumns(String alias, String propertyName) throws QueryException { return propertyMapping.toColumns( alias, propertyName ); } public String[] toColumns(String propertyName) throws QueryException { return propertyMapping.getColumnNames( propertyName ); } public Type toType(String propertyName) throws QueryException { return propertyMapping.toType( propertyName ); } public String[] getPropertyColumnNames(String propertyName) { return propertyMapping.getColumnNames( propertyName ); } /** * Warning: * When there are duplicated property names in the subclasses * of the class, this method may return the wrong table * number for the duplicated subclass property (note that * SingleTableEntityPersister defines an overloaded form * which takes the entity name. */ public int getSubclassPropertyTableNumber(String propertyPath) { String rootPropertyName = StringHelper.root(propertyPath); Type type = propertyMapping.toType(rootPropertyName); if ( type.isAssociationType() ) { AssociationType assocType = ( AssociationType ) type; if ( assocType.useLHSPrimaryKey() ) { // performance op to avoid the array search return 0; } else if ( type.isCollectionType() ) { // properly handle property-ref-based associations rootPropertyName = assocType.getLHSPropertyName(); } } //Enable for HHH-440, which we don't like: /*if ( type.isComponentType() && !propertyName.equals(rootPropertyName) ) { String unrooted = StringHelper.unroot(propertyName); int idx = ArrayHelper.indexOf( getSubclassColumnClosure(), unrooted ); if ( idx != -1 ) { return getSubclassColumnTableNumberClosure()[idx]; } }*/ int index = ArrayHelper.indexOf( getSubclassPropertyNameClosure(), rootPropertyName); //TODO: optimize this better! return index==-1 ? 0 : getSubclassPropertyTableNumber(index); } public Declarer getSubclassPropertyDeclarer(String propertyPath) { int tableIndex = getSubclassPropertyTableNumber( propertyPath ); if ( tableIndex == 0 ) { return Declarer.CLASS; } else if ( isClassOrSuperclassTable( tableIndex ) ) { return Declarer.SUPERCLASS; } else { return Declarer.SUBCLASS; } } protected String generateTableAlias(String rootAlias, int tableNumber) { if ( tableNumber == 0 ) { return rootAlias; } StringBuffer buf = new StringBuffer().append( rootAlias ); if ( !rootAlias.endsWith( "_" ) ) { buf.append( '_' ); } return buf.append( tableNumber ).append( '_' ).toString(); } public String[] toColumns(String name, final int i) { final String alias = generateTableAlias( name, getSubclassPropertyTableNumber( i ) ); String[] cols = getSubclassPropertyColumnNames( i ); String[] templates = getSubclassPropertyFormulaTemplateClosure()[i]; String[] result = new String[cols.length]; for ( int j = 0; j < cols.length; j++ ) { if ( cols[j] == null ) { result[j] = StringHelper.replace( templates[j], Template.TEMPLATE, alias ); } else { result[j] = StringHelper.qualify( alias, cols[j] ); } } return result; } private int getSubclassPropertyIndex(String propertyName) { return ArrayHelper.indexOf(subclassPropertyNameClosure, propertyName); } protected String[] getPropertySubclassNames() { return propertySubclassNames; } public String[] getPropertyColumnNames(int i) { return propertyColumnNames[i]; } protected int getPropertyColumnSpan(int i) { return propertyColumnSpans[i]; } protected boolean hasFormulaProperties() { return hasFormulaProperties; } public FetchMode getFetchMode(int i) { return subclassPropertyFetchModeClosure[i]; } public CascadeStyle getCascadeStyle(int i) { return subclassPropertyCascadeStyleClosure[i]; } public Type getSubclassPropertyType(int i) { return subclassPropertyTypeClosure[i]; } public String getSubclassPropertyName(int i) { return subclassPropertyNameClosure[i]; } public int countSubclassProperties() { return subclassPropertyTypeClosure.length; } public String[] getSubclassPropertyColumnNames(int i) { return subclassPropertyColumnNameClosure[i]; } public boolean isDefinedOnSubclass(int i) { return propertyDefinedOnSubclass[i]; } protected String[][] getSubclassPropertyFormulaTemplateClosure() { return subclassPropertyFormulaTemplateClosure; } protected Type[] getSubclassPropertyTypeClosure() { return subclassPropertyTypeClosure; } protected String[][] getSubclassPropertyColumnNameClosure() { return subclassPropertyColumnNameClosure; } protected String[] getSubclassPropertyNameClosure() { return subclassPropertyNameClosure; } protected String[] getSubclassPropertySubclassNameClosure() { return subclassPropertySubclassNameClosure; } protected String[] getSubclassColumnClosure() { return subclassColumnClosure; } protected String[] getSubclassColumnAliasClosure() { return subclassColumnAliasClosure; } protected String[] getSubclassFormulaClosure() { return subclassFormulaClosure; } protected String[] getSubclassFormulaTemplateClosure() { return subclassFormulaTemplateClosure; } protected String[] getSubclassFormulaAliasClosure() { return subclassFormulaAliasClosure; } public String[] getSubclassPropertyColumnAliases(String propertyName, String suffix) { String rawAliases[] = ( String[] ) subclassPropertyAliases.get( propertyName ); if ( rawAliases == null ) { return null; } String result[] = new String[rawAliases.length]; for ( int i = 0; i < rawAliases.length; i++ ) { result[i] = new Alias( suffix ).toUnquotedAliasString( rawAliases[i] ); } return result; } public String[] getSubclassPropertyColumnNames(String propertyName) { //TODO: should we allow suffixes on these ? return ( String[] ) subclassPropertyColumnNames.get( propertyName ); } //This is really ugly, but necessary: /** * Must be called by subclasses, at the end of their constructors */ protected void initSubclassPropertyAliasesMap(PersistentClass model) throws MappingException { // ALIASES internalInitSubclassPropertyAliasesMap( null, model.getSubclassPropertyClosureIterator() ); // aliases for identifier ( alias.id ); skip if the entity defines a non-id property named 'id' if ( ! entityMetamodel.hasNonIdentifierPropertyNamedId() ) { subclassPropertyAliases.put( ENTITY_ID, getIdentifierAliases() ); subclassPropertyColumnNames.put( ENTITY_ID, getIdentifierColumnNames() ); } // aliases named identifier ( alias.idname ) if ( hasIdentifierProperty() ) { subclassPropertyAliases.put( getIdentifierPropertyName(), getIdentifierAliases() ); subclassPropertyColumnNames.put( getIdentifierPropertyName(), getIdentifierColumnNames() ); } // aliases for composite-id's if ( getIdentifierType().isComponentType() ) { // Fetch embedded identifiers propertynames from the "virtual" identifier component AbstractComponentType componentId = ( AbstractComponentType ) getIdentifierType(); String[] idPropertyNames = componentId.getPropertyNames(); String[] idAliases = getIdentifierAliases(); String[] idColumnNames = getIdentifierColumnNames(); for ( int i = 0; i < idPropertyNames.length; i++ ) { if ( entityMetamodel.hasNonIdentifierPropertyNamedId() ) { subclassPropertyAliases.put( ENTITY_ID + "." + idPropertyNames[i], new String[] { idAliases[i] } ); subclassPropertyColumnNames.put( ENTITY_ID + "." + getIdentifierPropertyName() + "." + idPropertyNames[i], new String[] { idColumnNames[i] } ); } // if (hasIdentifierProperty() && !ENTITY_ID.equals( getIdentifierPropertyName() ) ) { if ( hasIdentifierProperty() ) { subclassPropertyAliases.put( getIdentifierPropertyName() + "." + idPropertyNames[i], new String[] { idAliases[i] } ); subclassPropertyColumnNames.put( getIdentifierPropertyName() + "." + idPropertyNames[i], new String[] { idColumnNames[i] } ); } else { // embedded composite ids ( alias.idname1, alias.idname2 ) subclassPropertyAliases.put( idPropertyNames[i], new String[] { idAliases[i] } ); subclassPropertyColumnNames.put( idPropertyNames[i], new String[] { idColumnNames[i] } ); } } } if ( entityMetamodel.isPolymorphic() ) { subclassPropertyAliases.put( ENTITY_CLASS, new String[] { getDiscriminatorAlias() } ); subclassPropertyColumnNames.put( ENTITY_CLASS, new String[] { getDiscriminatorColumnName() } ); } } private void internalInitSubclassPropertyAliasesMap(String path, Iterator propertyIterator) { while ( propertyIterator.hasNext() ) { Property prop = ( Property ) propertyIterator.next(); String propname = path == null ? prop.getName() : path + "." + prop.getName(); if ( prop.isComposite() ) { Component component = ( Component ) prop.getValue(); Iterator compProps = component.getPropertyIterator(); internalInitSubclassPropertyAliasesMap( propname, compProps ); } else { String[] aliases = new String[prop.getColumnSpan()]; String[] cols = new String[prop.getColumnSpan()]; Iterator colIter = prop.getColumnIterator(); int l = 0; while ( colIter.hasNext() ) { Selectable thing = ( Selectable ) colIter.next(); aliases[l] = thing.getAlias( getFactory().getDialect(), prop.getValue().getTable() ); cols[l] = thing.getText( getFactory().getDialect() ); // TODO: skip formulas? l++; } subclassPropertyAliases.put( propname, aliases ); subclassPropertyColumnNames.put( propname, cols ); } } } public Object loadByUniqueKey( String propertyName, Object uniqueKey, SessionImplementor session) throws HibernateException { return getAppropriateUniqueKeyLoader( propertyName, session ).loadByUniqueKey( session, uniqueKey ); } private EntityLoader getAppropriateUniqueKeyLoader(String propertyName, SessionImplementor session) { final boolean useStaticLoader = !session.getLoadQueryInfluencers().hasEnabledFilters() && !session.getLoadQueryInfluencers().hasEnabledFetchProfiles() && propertyName.indexOf('.')<0; //ugly little workaround for fact that createUniqueKeyLoaders() does not handle component properties if ( useStaticLoader ) { return ( EntityLoader ) uniqueKeyLoaders.get( propertyName ); } else { return createUniqueKeyLoader( propertyMapping.toType( propertyName ), propertyMapping.toColumns( propertyName ), session.getLoadQueryInfluencers() ); } } public int getPropertyIndex(String propertyName) { return entityMetamodel.getPropertyIndex(propertyName); } protected void createUniqueKeyLoaders() throws MappingException { Type[] propertyTypes = getPropertyTypes(); String[] propertyNames = getPropertyNames(); for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) { if ( propertyUniqueness[i] ) { //don't need filters for the static loaders uniqueKeyLoaders.put( propertyNames[i], createUniqueKeyLoader( propertyTypes[i], getPropertyColumnNames( i ), LoadQueryInfluencers.NONE ) ); //TODO: create uk loaders for component properties } } } private EntityLoader createUniqueKeyLoader( Type uniqueKeyType, String[] columns, LoadQueryInfluencers loadQueryInfluencers) { if ( uniqueKeyType.isEntityType() ) { String className = ( ( EntityType ) uniqueKeyType ).getAssociatedEntityName(); uniqueKeyType = getFactory().getEntityPersister( className ).getIdentifierType(); } return new EntityLoader( this, columns, uniqueKeyType, 1, LockMode.NONE, getFactory(), loadQueryInfluencers ); } protected String getSQLWhereString(String alias) { return StringHelper.replace( sqlWhereStringTemplate, Template.TEMPLATE, alias ); } protected boolean hasWhere() { return sqlWhereString != null; } private void initOrdinaryPropertyPaths(Mapping mapping) throws MappingException { for ( int i = 0; i < getSubclassPropertyNameClosure().length; i++ ) { propertyMapping.initPropertyPaths( getSubclassPropertyNameClosure()[i], getSubclassPropertyTypeClosure()[i], getSubclassPropertyColumnNameClosure()[i], getSubclassPropertyFormulaTemplateClosure()[i], mapping ); } } private void initIdentifierPropertyPaths(Mapping mapping) throws MappingException { String idProp = getIdentifierPropertyName(); if ( idProp != null ) { propertyMapping.initPropertyPaths( idProp, getIdentifierType(), getIdentifierColumnNames(), null, mapping ); } if ( entityMetamodel.getIdentifierProperty().isEmbedded() ) { propertyMapping.initPropertyPaths( null, getIdentifierType(), getIdentifierColumnNames(), null, mapping ); } if ( ! entityMetamodel.hasNonIdentifierPropertyNamedId() ) { propertyMapping.initPropertyPaths( ENTITY_ID, getIdentifierType(), getIdentifierColumnNames(), null, mapping ); } } private void initDiscriminatorPropertyPath(Mapping mapping) throws MappingException { propertyMapping.initPropertyPaths( ENTITY_CLASS, getDiscriminatorType(), new String[]{getDiscriminatorColumnName()}, new String[]{getDiscriminatorFormulaTemplate()}, getFactory() ); } protected void initPropertyPaths(Mapping mapping) throws MappingException { initOrdinaryPropertyPaths(mapping); initOrdinaryPropertyPaths(mapping); //do two passes, for collection property-ref! initIdentifierPropertyPaths(mapping); if ( entityMetamodel.isPolymorphic() ) { initDiscriminatorPropertyPath( mapping ); } } protected UniqueEntityLoader createEntityLoader( LockMode lockMode, LoadQueryInfluencers loadQueryInfluencers) throws MappingException { //TODO: disable batch loading if lockMode > READ? return BatchingEntityLoader.createBatchingEntityLoader( this, batchSize, lockMode, getFactory(), loadQueryInfluencers ); } protected UniqueEntityLoader createEntityLoader(LockMode lockMode) throws MappingException { return createEntityLoader( lockMode, LoadQueryInfluencers.NONE ); } protected boolean check(int rows, Serializable id, int tableNumber, Expectation expectation, PreparedStatement statement) throws HibernateException { try { expectation.verifyOutcome( rows, statement, -1 ); } catch( StaleStateException e ) { if ( !isNullableTable( tableNumber ) ) { if ( getFactory().getStatistics().isStatisticsEnabled() ) { getFactory().getStatisticsImplementor() .optimisticFailure( getEntityName() ); } throw new StaleObjectStateException( getEntityName(), id ); } return false; } catch( TooManyRowsAffectedException e ) { throw new HibernateException( "Duplicate identifier in table for: " + MessageHelper.infoString( this, id, getFactory() ) ); } catch ( Throwable t ) { return false; } return true; } protected String generateUpdateString(boolean[] includeProperty, int j, boolean useRowId) { return generateUpdateString( includeProperty, j, null, useRowId ); } /** * Generate the SQL that updates a row by id (and version) */ protected String generateUpdateString(final boolean[] includeProperty, final int j, final Object[] oldFields, final boolean useRowId) { Update update = new Update( getFactory().getDialect() ).setTableName( getTableName( j ) ); // select the correct row by either pk or rowid if ( useRowId ) { update.setPrimaryKeyColumnNames( new String[]{rowIdName} ); //TODO: eventually, rowIdName[j] } else { update.setPrimaryKeyColumnNames( getKeyColumns( j ) ); } boolean hasColumns = false; for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) { if ( includeProperty[i] && isPropertyOfTable( i, j ) ) { // this is a property of the table, which we are updating update.addColumns( getPropertyColumnNames(i), propertyColumnUpdateable[i] ); hasColumns = hasColumns || getPropertyColumnSpan( i ) > 0; } } if ( j == 0 && isVersioned() && entityMetamodel.getOptimisticLockMode() == Versioning.OPTIMISTIC_LOCK_VERSION ) { // this is the root (versioned) table, and we are using version-based // optimistic locking; if we are not updating the version, also don't // check it (unless this is a "generated" version column)! if ( checkVersion( includeProperty ) ) { update.setVersionColumnName( getVersionColumnName() ); hasColumns = true; } } else if ( entityMetamodel.getOptimisticLockMode() > Versioning.OPTIMISTIC_LOCK_VERSION && oldFields != null ) { // we are using "all" or "dirty" property-based optimistic locking boolean[] includeInWhere = entityMetamodel.getOptimisticLockMode() == Versioning.OPTIMISTIC_LOCK_ALL ? getPropertyUpdateability() : //optimistic-lock="all", include all updatable properties includeProperty; //optimistic-lock="dirty", include all properties we are updating this time boolean[] versionability = getPropertyVersionability(); Type[] types = getPropertyTypes(); for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) { boolean include = includeInWhere[i] && isPropertyOfTable( i, j ) && versionability[i]; if ( include ) { // this property belongs to the table, and it is not specifically // excluded from optimistic locking by optimistic-lock="false" String[] propertyColumnNames = getPropertyColumnNames( i ); boolean[] propertyNullness = types[i].toColumnNullness( oldFields[i], getFactory() ); for ( int k=0; k<propertyNullness.length; k++ ) { if ( propertyNullness[k] ) { update.addWhereColumn( propertyColumnNames[k] ); } else { update.addWhereColumn( propertyColumnNames[k], " is null" ); } } } } } if ( getFactory().getSettings().isCommentsEnabled() ) { update.setComment( "update " + getEntityName() ); } return hasColumns ? update.toStatementString() : null; } private boolean checkVersion(final boolean[] includeProperty) { return includeProperty[ getVersionProperty() ] || entityMetamodel.getPropertyUpdateGenerationInclusions()[ getVersionProperty() ] != ValueInclusion.NONE; } protected String generateInsertString(boolean[] includeProperty, int j) { return generateInsertString( false, includeProperty, j ); } protected String generateInsertString(boolean identityInsert, boolean[] includeProperty) { return generateInsertString( identityInsert, includeProperty, 0 ); } /** * Generate the SQL that inserts a row */ protected String generateInsertString(boolean identityInsert, boolean[] includeProperty, int j) { // todo : remove the identityInsert param and variations; // identity-insert strings are now generated from generateIdentityInsertString() Insert insert = new Insert( getFactory().getDialect() ) .setTableName( getTableName( j ) ); // add normal properties for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) { if ( includeProperty[i] && isPropertyOfTable( i, j ) ) { // this property belongs on the table and is to be inserted insert.addColumns( getPropertyColumnNames(i), propertyColumnInsertable[i] ); } } // add the discriminator if ( j == 0 ) { addDiscriminatorToInsert( insert ); } // add the primary key if ( j == 0 && identityInsert ) { insert.addIdentityColumn( getKeyColumns( 0 )[0] ); } else { insert.addColumns( getKeyColumns( j ) ); } if ( getFactory().getSettings().isCommentsEnabled() ) { insert.setComment( "insert " + getEntityName() ); } String result = insert.toStatementString(); // append the SQL to return the generated identifier if ( j == 0 && identityInsert && useInsertSelectIdentity() ) { //TODO: suck into Insert result = getFactory().getDialect().appendIdentitySelectToInsert( result ); } return result; } /** * Used to generate an insery statement against the root table in the * case of identifier generation strategies where the insert statement * executions actually generates the identifier value. * * @param includeProperty indices of the properties to include in the * insert statement. * @return The insert SQL statement string */ protected String generateIdentityInsertString(boolean[] includeProperty) { Insert insert = identityDelegate.prepareIdentifierGeneratingInsert(); insert.setTableName( getTableName( 0 ) ); // add normal properties for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) { if ( includeProperty[i] && isPropertyOfTable( i, 0 ) ) { // this property belongs on the table and is to be inserted insert.addColumns( getPropertyColumnNames(i), propertyColumnInsertable[i] ); } } // add the discriminator addDiscriminatorToInsert( insert ); // delegate already handles PK columns if ( getFactory().getSettings().isCommentsEnabled() ) { insert.setComment( "insert " + getEntityName() ); } return insert.toStatementString(); } /** * Generate the SQL that deletes a row by id (and version) */ protected String generateDeleteString(int j) { Delete delete = new Delete() .setTableName( getTableName( j ) ) .setPrimaryKeyColumnNames( getKeyColumns( j ) ); if ( j == 0 ) { delete.setVersionColumnName( getVersionColumnName() ); } if ( getFactory().getSettings().isCommentsEnabled() ) { delete.setComment( "delete " + getEntityName() ); } return delete.toStatementString(); } protected int dehydrate( Serializable id, Object[] fields, boolean[] includeProperty, boolean[][] includeColumns, int j, PreparedStatement st, SessionImplementor session) throws HibernateException, SQLException { return dehydrate( id, fields, null, includeProperty, includeColumns, j, st, session, 1 ); } /** * Marshall the fields of a persistent instance to a prepared statement */ protected int dehydrate( final Serializable id, final Object[] fields, final Object rowId, final boolean[] includeProperty, final boolean[][] includeColumns, final int j, final PreparedStatement ps, final SessionImplementor session, int index) throws SQLException, HibernateException { if ( log.isTraceEnabled() ) { log.trace( "Dehydrating entity: " + MessageHelper.infoString( this, id, getFactory() ) ); } for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) { if ( includeProperty[i] && isPropertyOfTable( i, j ) ) { getPropertyTypes()[i].nullSafeSet( ps, fields[i], index, includeColumns[i], session ); //index += getPropertyColumnSpan( i ); index += ArrayHelper.countTrue( includeColumns[i] ); //TODO: this is kinda slow... } } if ( rowId != null ) { ps.setObject( index, rowId ); index += 1; } else if ( id != null ) { getIdentifierType().nullSafeSet( ps, id, index, session ); index += getIdentifierColumnSpan(); } return index; } /** * Unmarshall the fields of a persistent instance from a result set, * without resolving associations or collections. Question: should * this really be here, or should it be sent back to Loader? */ public Object[] hydrate( final ResultSet rs, final Serializable id, final Object object, final Loadable rootLoadable, final String[][] suffixedPropertyColumns, final boolean allProperties, final SessionImplementor session) throws SQLException, HibernateException { if ( log.isTraceEnabled() ) { log.trace( "Hydrating entity: " + MessageHelper.infoString( this, id, getFactory() ) ); } final AbstractEntityPersister rootPersister = (AbstractEntityPersister) rootLoadable; final boolean hasDeferred = rootPersister.hasSequentialSelect(); PreparedStatement sequentialSelect = null; ResultSet sequentialResultSet = null; boolean sequentialSelectEmpty = false; try { if ( hasDeferred ) { final String sql = rootPersister.getSequentialSelect( getEntityName() ); if ( sql != null ) { //TODO: I am not so sure about the exception handling in this bit! sequentialSelect = session.getBatcher().prepareSelectStatement( sql ); rootPersister.getIdentifierType().nullSafeSet( sequentialSelect, id, 1, session ); sequentialResultSet = sequentialSelect.executeQuery(); if ( !sequentialResultSet.next() ) { // TODO: Deal with the "optional" attribute in the <join> mapping; // this code assumes that optional defaults to "true" because it // doesn't actually seem to work in the fetch="join" code // // Note that actual proper handling of optional-ality here is actually // more involved than this patch assumes. Remember that we might have // multiple <join/> mappings associated with a single entity. Really // a couple of things need to happen to properly handle optional here: // 1) First and foremost, when handling multiple <join/>s, we really // should be using the entity root table as the driving table; // another option here would be to choose some non-optional joined // table to use as the driving table. In all likelihood, just using // the root table is much simplier // 2) Need to add the FK columns corresponding to each joined table // to the generated select list; these would then be used when // iterating the result set to determine whether all non-optional // data is present // My initial thoughts on the best way to deal with this would be // to introduce a new SequentialSelect abstraction that actually gets // generated in the persisters (ok, SingleTable...) and utilized here. // It would encapsulated all this required optional-ality checking... sequentialSelectEmpty = true; } } } final String[] propNames = getPropertyNames(); final Type[] types = getPropertyTypes(); final Object[] values = new Object[types.length]; final boolean[] laziness = getPropertyLaziness(); final String[] propSubclassNames = getSubclassPropertySubclassNameClosure(); for ( int i = 0; i < types.length; i++ ) { if ( !propertySelectable[i] ) { values[i] = BackrefPropertyAccessor.UNKNOWN; } else if ( allProperties || !laziness[i] ) { //decide which ResultSet to get the property value from: final boolean propertyIsDeferred = hasDeferred && rootPersister.isSubclassPropertyDeferred( propNames[i], propSubclassNames[i] ); if ( propertyIsDeferred && sequentialSelectEmpty ) { values[i] = null; } else { final ResultSet propertyResultSet = propertyIsDeferred ? sequentialResultSet : rs; final String[] cols = propertyIsDeferred ? propertyColumnAliases[i] : suffixedPropertyColumns[i]; values[i] = types[i].hydrate( propertyResultSet, cols, session, object ); } } else { values[i] = LazyPropertyInitializer.UNFETCHED_PROPERTY; } } if ( sequentialResultSet != null ) { sequentialResultSet.close(); } return values; } finally { if ( sequentialSelect != null ) { session.getBatcher().closeStatement( sequentialSelect ); } } } protected boolean useInsertSelectIdentity() { return !useGetGeneratedKeys() && getFactory().getDialect().supportsInsertSelectIdentity(); } protected boolean useGetGeneratedKeys() { return getFactory().getSettings().isGetGeneratedKeysEnabled(); } protected String getSequentialSelect(String entityName) { throw new UnsupportedOperationException("no sequential selects"); } /** * Perform an SQL INSERT, and then retrieve a generated identifier. * <p/> * This form is used for PostInsertIdentifierGenerator-style ids (IDENTITY, * select, etc). */ protected Serializable insert( final Object[] fields, final boolean[] notNull, String sql, final Object object, final SessionImplementor session) throws HibernateException { if ( log.isTraceEnabled() ) { log.trace( "Inserting entity: " + getEntityName() + " (native id)" ); if ( isVersioned() ) { log.trace( "Version: " + Versioning.getVersion( fields, this ) ); } } Binder binder = new Binder() { public void bindValues(PreparedStatement ps) throws SQLException { dehydrate( null, fields, notNull, propertyColumnInsertable, 0, ps, session ); } public Object getEntity() { return object; } }; return identityDelegate.performInsert( sql, session, binder ); } public String getIdentitySelectString() { //TODO: cache this in an instvar return getFactory().getDialect().getIdentitySelectString( getTableName(0), getKeyColumns(0)[0], getIdentifierType().sqlTypes( getFactory() )[0] ); } public String getSelectByUniqueKeyString(String propertyName) { return new SimpleSelect( getFactory().getDialect() ) .setTableName( getTableName(0) ) .addColumns( getKeyColumns(0) ) .addCondition( getPropertyColumnNames(propertyName), "=?" ) .toStatementString(); } /** * Perform an SQL INSERT. * <p/> * This for is used for all non-root tables as well as the root table * in cases where the identifier value is known before the insert occurs. */ protected void insert( final Serializable id, final Object[] fields, final boolean[] notNull, final int j, final String sql, final Object object, final SessionImplementor session) throws HibernateException { if ( isInverseTable( j ) ) { return; } //note: it is conceptually possible that a UserType could map null to // a non-null value, so the following is arguable: if ( isNullableTable( j ) && isAllNull( fields, j ) ) { return; } if ( log.isTraceEnabled() ) { log.trace( "Inserting entity: " + MessageHelper.infoString( this, id, getFactory() ) ); if ( j == 0 && isVersioned() ) { log.trace( "Version: " + Versioning.getVersion( fields, this ) ); } } Expectation expectation = Expectations.appropriateExpectation( insertResultCheckStyles[j] ); boolean callable = isInsertCallable( j ); // we can't batch joined inserts, *especially* not if it is an identity insert; // nor can we batch statements where the expectation is based on an output param final boolean useBatch = j == 0 && expectation.canBeBatched(); try { // Render the SQL query final PreparedStatement insert; if ( useBatch ) { if ( callable ) { insert = session.getBatcher().prepareBatchCallableStatement( sql ); } else { insert = session.getBatcher().prepareBatchStatement( sql ); } } else { if ( callable ) { insert = session.getBatcher().prepareCallableStatement( sql ); } else { insert = session.getBatcher().prepareStatement( sql ); } } try { int index = 1; index += expectation.prepare( insert ); // Write the values of fields onto the prepared statement - we MUST use the state at the time the // insert was issued (cos of foreign key constraints). Not necessarily the object's current state dehydrate( id, fields, null, notNull, propertyColumnInsertable, j, insert, session, index ); if ( useBatch ) { // TODO : shouldnt inserts be Expectations.NONE? session.getBatcher().addToBatch( expectation ); } else { expectation.verifyOutcome( insert.executeUpdate(), insert, -1 ); } } catch ( SQLException sqle ) { if ( useBatch ) { session.getBatcher().abortBatch( sqle ); } throw sqle; } finally { if ( !useBatch ) { session.getBatcher().closeStatement( insert ); } } } catch ( SQLException sqle ) { throw JDBCExceptionHelper.convert( getFactory().getSQLExceptionConverter(), sqle, "could not insert: " + MessageHelper.infoString( this ), sql ); } } /** * Perform an SQL UPDATE or SQL INSERT */ protected void updateOrInsert( final Serializable id, final Object[] fields, final Object[] oldFields, final Object rowId, final boolean[] includeProperty, final int j, final Object oldVersion, final Object object, final String sql, final SessionImplementor session) throws HibernateException { if ( !isInverseTable( j ) ) { final boolean isRowToUpdate; if ( isNullableTable( j ) && oldFields != null && isAllNull( oldFields, j ) ) { //don't bother trying to update, we know there is no row there yet isRowToUpdate = false; } else if ( isNullableTable( j ) && isAllNull( fields, j ) ) { //if all fields are null, we might need to delete existing row isRowToUpdate = true; delete( id, oldVersion, j, object, getSQLDeleteStrings()[j], session, null ); } else { //there is probably a row there, so try to update //if no rows were updated, we will find out isRowToUpdate = update( id, fields, oldFields, rowId, includeProperty, j, oldVersion, object, sql, session ); } if ( !isRowToUpdate && !isAllNull( fields, j ) ) { // assume that the row was not there since it previously had only null // values, so do an INSERT instead //TODO: does not respect dynamic-insert insert( id, fields, getPropertyInsertability(), j, getSQLInsertStrings()[j], object, session ); } } } protected boolean update( final Serializable id, final Object[] fields, final Object[] oldFields, final Object rowId, final boolean[] includeProperty, final int j, final Object oldVersion, final Object object, final String sql, final SessionImplementor session) throws HibernateException { final boolean useVersion = j == 0 && isVersioned(); final Expectation expectation = Expectations.appropriateExpectation( updateResultCheckStyles[j] ); final boolean callable = isUpdateCallable( j ); final boolean useBatch = j == 0 && expectation.canBeBatched() && isBatchable(); //note: updates to joined tables can't be batched... if ( log.isTraceEnabled() ) { log.trace( "Updating entity: " + MessageHelper.infoString( this, id, getFactory() ) ); if ( useVersion ) { log.trace( "Existing version: " + oldVersion + " -> New version: " + fields[getVersionProperty()] ); } } try { int index = 1; // starting index final PreparedStatement update; if ( useBatch ) { if ( callable ) { update = session.getBatcher().prepareBatchCallableStatement( sql ); } else { update = session.getBatcher().prepareBatchStatement( sql ); } } else { if ( callable ) { update = session.getBatcher().prepareCallableStatement( sql ); } else { update = session.getBatcher().prepareStatement( sql ); } } try { index+= expectation.prepare( update ); //Now write the values of fields onto the prepared statement index = dehydrate( id, fields, rowId, includeProperty, propertyColumnUpdateable, j, update, session, index ); // Write any appropriate versioning conditional parameters if ( useVersion && Versioning.OPTIMISTIC_LOCK_VERSION == entityMetamodel.getOptimisticLockMode() ) { if ( checkVersion( includeProperty ) ) { getVersionType().nullSafeSet( update, oldVersion, index, session ); } } else if ( entityMetamodel.getOptimisticLockMode() > Versioning.OPTIMISTIC_LOCK_VERSION && oldFields != null ) { boolean[] versionability = getPropertyVersionability(); //TODO: is this really necessary???? boolean[] includeOldField = entityMetamodel.getOptimisticLockMode() == Versioning.OPTIMISTIC_LOCK_ALL ? getPropertyUpdateability() : includeProperty; Type[] types = getPropertyTypes(); for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) { boolean include = includeOldField[i] && isPropertyOfTable( i, j ) && versionability[i]; //TODO: is this really necessary???? if ( include ) { boolean[] settable = types[i].toColumnNullness( oldFields[i], getFactory() ); types[i].nullSafeSet( update, oldFields[i], index, settable, session ); index += ArrayHelper.countTrue(settable); } } } if ( useBatch ) { session.getBatcher().addToBatch( expectation ); return true; } else { return check( update.executeUpdate(), id, j, expectation, update ); } } catch ( SQLException sqle ) { if ( useBatch ) { session.getBatcher().abortBatch( sqle ); } throw sqle; } finally { if ( !useBatch ) { session.getBatcher().closeStatement( update ); } } } catch ( SQLException sqle ) { throw JDBCExceptionHelper.convert( getFactory().getSQLExceptionConverter(), sqle, "could not update: " + MessageHelper.infoString( this, id, getFactory() ), sql ); } } /** * Perform an SQL DELETE */ protected void delete( final Serializable id, final Object version, final int j, final Object object, final String sql, final SessionImplementor session, final Object[] loadedState) throws HibernateException { if ( isInverseTable( j ) ) { return; } final boolean useVersion = j == 0 && isVersioned(); final boolean callable = isDeleteCallable( j ); final Expectation expectation = Expectations.appropriateExpectation( deleteResultCheckStyles[j] ); final boolean useBatch = j == 0 && isBatchable() && expectation.canBeBatched(); if ( log.isTraceEnabled() ) { log.trace( "Deleting entity: " + MessageHelper.infoString( this, id, getFactory() ) ); if ( useVersion ) { log.trace( "Version: " + version ); } } if ( isTableCascadeDeleteEnabled( j ) ) { if ( log.isTraceEnabled() ) { log.trace( "delete handled by foreign key constraint: " + getTableName( j ) ); } return; //EARLY EXIT! } try { //Render the SQL query PreparedStatement delete; int index = 1; if ( useBatch ) { if ( callable ) { delete = session.getBatcher().prepareBatchCallableStatement( sql ); } else { delete = session.getBatcher().prepareBatchStatement( sql ); } } else { if ( callable ) { delete = session.getBatcher().prepareCallableStatement( sql ); } else { delete = session.getBatcher().prepareStatement( sql ); } } try { index += expectation.prepare( delete ); // Do the key. The key is immutable so we can use the _current_ object state - not necessarily // the state at the time the delete was issued getIdentifierType().nullSafeSet( delete, id, index, session ); index += getIdentifierColumnSpan(); // We should use the _current_ object state (ie. after any updates that occurred during flush) if ( useVersion ) { getVersionType().nullSafeSet( delete, version, index, session ); } else if ( entityMetamodel.getOptimisticLockMode() > Versioning.OPTIMISTIC_LOCK_VERSION && loadedState != null ) { boolean[] versionability = getPropertyVersionability(); Type[] types = getPropertyTypes(); for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) { if ( isPropertyOfTable( i, j ) && versionability[i] ) { // this property belongs to the table and it is not specifically // excluded from optimistic locking by optimistic-lock="false" boolean[] settable = types[i].toColumnNullness( loadedState[i], getFactory() ); types[i].nullSafeSet( delete, loadedState[i], index, settable, session ); index += ArrayHelper.countTrue( settable ); } } } if ( useBatch ) { session.getBatcher().addToBatch( expectation ); } else { check( delete.executeUpdate(), id, j, expectation, delete ); } } catch ( SQLException sqle ) { if ( useBatch ) { session.getBatcher().abortBatch( sqle ); } throw sqle; } finally { if ( !useBatch ) { session.getBatcher().closeStatement( delete ); } } } catch ( SQLException sqle ) { throw JDBCExceptionHelper.convert( getFactory().getSQLExceptionConverter(), sqle, "could not delete: " + MessageHelper.infoString( this, id, getFactory() ), sql ); } } private String[] getUpdateStrings(boolean byRowId, boolean lazy) { if ( byRowId ) { return lazy ? getSQLLazyUpdateByRowIdStrings() : getSQLUpdateByRowIdStrings(); } else { return lazy ? getSQLLazyUpdateStrings() : getSQLUpdateStrings(); } } /** * Update an object */ public void update( final Serializable id, final Object[] fields, final int[] dirtyFields, final boolean hasDirtyCollection, final Object[] oldFields, final Object oldVersion, final Object object, final Object rowId, final SessionImplementor session) throws HibernateException { //note: dirtyFields==null means we had no snapshot, and we couldn't get one using select-before-update // oldFields==null just means we had no snapshot to begin with (we might have used select-before-update to get the dirtyFields) final boolean[] tableUpdateNeeded = getTableUpdateNeeded( dirtyFields, hasDirtyCollection ); final int span = getTableSpan(); final boolean[] propsToUpdate; final String[] updateStrings; if ( entityMetamodel.isDynamicUpdate() && dirtyFields != null ) { // For the case of dynamic-update="true", we need to generate the UPDATE SQL propsToUpdate = getPropertiesToUpdate( dirtyFields, hasDirtyCollection ); // don't need to check laziness (dirty checking algorithm handles that) updateStrings = new String[span]; for ( int j = 0; j < span; j++ ) { updateStrings[j] = tableUpdateNeeded[j] ? generateUpdateString( propsToUpdate, j, oldFields, j == 0 && rowId != null ) : null; } } else { // For the case of dynamic-update="false", or no snapshot, we use the static SQL updateStrings = getUpdateStrings( rowId != null, hasUninitializedLazyProperties( object, session.getEntityMode() ) ); propsToUpdate = getPropertyUpdateability( object, session.getEntityMode() ); } for ( int j = 0; j < span; j++ ) { // Now update only the tables with dirty properties (and the table with the version number) if ( tableUpdateNeeded[j] ) { updateOrInsert( id, fields, oldFields, j == 0 ? rowId : null, propsToUpdate, j, oldVersion, object, updateStrings[j], session ); } } } public Serializable insert(Object[] fields, Object object, SessionImplementor session) throws HibernateException { final int span = getTableSpan(); final Serializable id; if ( entityMetamodel.isDynamicInsert() ) { // For the case of dynamic-insert="true", we need to generate the INSERT SQL boolean[] notNull = getPropertiesToInsert( fields ); id = insert( fields, notNull, generateInsertString( true, notNull ), object, session ); for ( int j = 1; j < span; j++ ) { insert( id, fields, notNull, j, generateInsertString( notNull, j ), object, session ); } } else { // For the case of dynamic-insert="false", use the static SQL id = insert( fields, getPropertyInsertability(), getSQLIdentityInsertString(), object, session ); for ( int j = 1; j < span; j++ ) { insert( id, fields, getPropertyInsertability(), j, getSQLInsertStrings()[j], object, session ); } } return id; } public void insert(Serializable id, Object[] fields, Object object, SessionImplementor session) throws HibernateException { final int span = getTableSpan(); if ( entityMetamodel.isDynamicInsert() ) { // For the case of dynamic-insert="true", we need to generate the INSERT SQL boolean[] notNull = getPropertiesToInsert( fields ); for ( int j = 0; j < span; j++ ) { insert( id, fields, notNull, j, generateInsertString( notNull, j ), object, session ); } } else { // For the case of dynamic-insert="false", use the static SQL for ( int j = 0; j < span; j++ ) { insert( id, fields, getPropertyInsertability(), j, getSQLInsertStrings()[j], object, session ); } } } /** * Delete an object */ public void delete(Serializable id, Object version, Object object, SessionImplementor session) throws HibernateException { final int span = getTableSpan(); boolean isImpliedOptimisticLocking = !entityMetamodel.isVersioned() && entityMetamodel.getOptimisticLockMode() > Versioning.OPTIMISTIC_LOCK_VERSION; Object[] loadedState = null; if ( isImpliedOptimisticLocking ) { // need to treat this as if it where optimistic-lock="all" (dirty does *not* make sense); // first we need to locate the "loaded" state // // Note, it potentially could be a proxy, so perform the location the safe way... EntityKey key = new EntityKey( id, this, session.getEntityMode() ); Object entity = session.getPersistenceContext().getEntity( key ); if ( entity != null ) { EntityEntry entry = session.getPersistenceContext().getEntry( entity ); loadedState = entry.getLoadedState(); } } final String[] deleteStrings; if ( isImpliedOptimisticLocking && loadedState != null ) { // we need to utilize dynamic delete statements deleteStrings = generateSQLDeletStrings( loadedState ); } else { // otherwise, utilize the static delete statements deleteStrings = getSQLDeleteStrings(); } for ( int j = span - 1; j >= 0; j-- ) { delete( id, version, j, object, deleteStrings[j], session, loadedState ); } } private String[] generateSQLDeletStrings(Object[] loadedState) { int span = getTableSpan(); String[] deleteStrings = new String[span]; for ( int j = span - 1; j >= 0; j-- ) { Delete delete = new Delete() .setTableName( getTableName( j ) ) .setPrimaryKeyColumnNames( getKeyColumns( j ) ); if ( getFactory().getSettings().isCommentsEnabled() ) { delete.setComment( "delete " + getEntityName() + " [" + j + "]" ); } boolean[] versionability = getPropertyVersionability(); Type[] types = getPropertyTypes(); for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) { if ( isPropertyOfTable( i, j ) && versionability[i] ) { // this property belongs to the table and it is not specifically // excluded from optimistic locking by optimistic-lock="false" String[] propertyColumnNames = getPropertyColumnNames( i ); boolean[] propertyNullness = types[i].toColumnNullness( loadedState[i], getFactory() ); for ( int k = 0; k < propertyNullness.length; k++ ) { if ( propertyNullness[k] ) { delete.addWhereFragment( propertyColumnNames[k] + " = ?" ); } else { delete.addWhereFragment( propertyColumnNames[k] + " is null" ); } } } } deleteStrings[j] = delete.toStatementString(); } return deleteStrings; } protected void logStaticSQL() { if ( log.isDebugEnabled() ) { log.debug( "Static SQL for entity: " + getEntityName() ); if ( sqlLazySelectString != null ) { log.debug( " Lazy select: " + sqlLazySelectString ); } if ( sqlVersionSelectString != null ) { log.debug( " Version select: " + sqlVersionSelectString ); } if ( sqlSnapshotSelectString != null ) { log.debug( " Snapshot select: " + sqlSnapshotSelectString ); } for ( int j = 0; j < getTableSpan(); j++ ) { log.debug( " Insert " + j + ": " + getSQLInsertStrings()[j] ); log.debug( " Update " + j + ": " + getSQLUpdateStrings()[j] ); log.debug( " Delete " + j + ": " + getSQLDeleteStrings()[j] ); } if ( sqlIdentityInsertString != null ) { log.debug( " Identity insert: " + sqlIdentityInsertString ); } if ( sqlUpdateByRowIdString != null ) { log.debug( " Update by row id (all fields): " + sqlUpdateByRowIdString ); } if ( sqlLazyUpdateByRowIdString != null ) { log.debug( " Update by row id (non-lazy fields): " + sqlLazyUpdateByRowIdString ); } if ( sqlInsertGeneratedValuesSelectString != null ) { log.debug( "Insert-generated property select: " + sqlInsertGeneratedValuesSelectString ); } if ( sqlUpdateGeneratedValuesSelectString != null ) { log.debug( "Update-generated property select: " + sqlUpdateGeneratedValuesSelectString ); } } } public String filterFragment(String alias, Map enabledFilters) throws MappingException { final StringBuffer sessionFilterFragment = new StringBuffer(); filterHelper.render( sessionFilterFragment, generateFilterConditionAlias( alias ), enabledFilters ); return sessionFilterFragment.append( filterFragment( alias ) ).toString(); } public String generateFilterConditionAlias(String rootAlias) { return rootAlias; } public String oneToManyFilterFragment(String alias) throws MappingException { return ""; } public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) { return getSubclassTableSpan() == 1 ? "" : //just a performance opt! createJoin( alias, innerJoin, includeSubclasses ).toFromFragmentString(); } public String whereJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) { return getSubclassTableSpan() == 1 ? "" : //just a performance opt! createJoin( alias, innerJoin, includeSubclasses ).toWhereFragmentString(); } protected boolean isSubclassTableLazy(int j) { return false; } protected JoinFragment createJoin(String name, boolean innerJoin, boolean includeSubclasses) { final String[] idCols = StringHelper.qualify( name, getIdentifierColumnNames() ); //all joins join to the pk of the driving table final JoinFragment join = getFactory().getDialect().createOuterJoinFragment(); final int tableSpan = getSubclassTableSpan(); for ( int j = 1; j < tableSpan; j++ ) { //notice that we skip the first table; it is the driving table! final boolean joinIsIncluded = isClassOrSuperclassTable( j ) || ( includeSubclasses && !isSubclassTableSequentialSelect( j ) && !isSubclassTableLazy( j ) ); if ( joinIsIncluded ) { join.addJoin( getSubclassTableName( j ), generateTableAlias( name, j ), idCols, getSubclassTableKeyColumns( j ), innerJoin && isClassOrSuperclassTable( j ) && !isInverseTable( j ) && !isNullableTable( j ) ? JoinFragment.INNER_JOIN : //we can inner join to superclass tables (the row MUST be there) JoinFragment.LEFT_OUTER_JOIN //we can never inner join to subclass tables ); } } return join; } protected JoinFragment createJoin(int[] tableNumbers, String drivingAlias) { final String[] keyCols = StringHelper.qualify( drivingAlias, getSubclassTableKeyColumns( tableNumbers[0] ) ); final JoinFragment jf = getFactory().getDialect().createOuterJoinFragment(); for ( int i = 1; i < tableNumbers.length; i++ ) { //skip the driving table final int j = tableNumbers[i]; jf.addJoin( getSubclassTableName( j ), generateTableAlias( getRootAlias(), j ), keyCols, getSubclassTableKeyColumns( j ), isInverseSubclassTable( j ) || isNullableSubclassTable( j ) ? JoinFragment.LEFT_OUTER_JOIN : JoinFragment.INNER_JOIN ); } return jf; } protected SelectFragment createSelect(final int[] subclassColumnNumbers, final int[] subclassFormulaNumbers) { SelectFragment selectFragment = new SelectFragment(); int[] columnTableNumbers = getSubclassColumnTableNumberClosure(); String[] columnAliases = getSubclassColumnAliasClosure(); String[] columns = getSubclassColumnClosure(); for ( int i = 0; i < subclassColumnNumbers.length; i++ ) { if ( subclassColumnSelectableClosure[i] ) { int columnNumber = subclassColumnNumbers[i]; final String subalias = generateTableAlias( getRootAlias(), columnTableNumbers[columnNumber] ); selectFragment.addColumn( subalias, columns[columnNumber], columnAliases[columnNumber] ); } } int[] formulaTableNumbers = getSubclassFormulaTableNumberClosure(); String[] formulaTemplates = getSubclassFormulaTemplateClosure(); String[] formulaAliases = getSubclassFormulaAliasClosure(); for ( int i = 0; i < subclassFormulaNumbers.length; i++ ) { int formulaNumber = subclassFormulaNumbers[i]; final String subalias = generateTableAlias( getRootAlias(), formulaTableNumbers[formulaNumber] ); selectFragment.addFormula( subalias, formulaTemplates[formulaNumber], formulaAliases[formulaNumber] ); } return selectFragment; } protected String createFrom(int tableNumber, String alias) { return getSubclassTableName( tableNumber ) + ' ' + alias; } protected String createWhereByKey(int tableNumber, String alias) { //TODO: move to .sql package, and refactor with similar things! return StringHelper.join( "=? and ", StringHelper.qualify( alias, getSubclassTableKeyColumns( tableNumber ) ) ) + "=?"; } protected String renderSelect( final int[] tableNumbers, final int[] columnNumbers, final int[] formulaNumbers) { Arrays.sort( tableNumbers ); //get 'em in the right order (not that it really matters) //render the where and from parts int drivingTable = tableNumbers[0]; final String drivingAlias = generateTableAlias( getRootAlias(), drivingTable ); //we *could* regerate this inside each called method! final String where = createWhereByKey( drivingTable, drivingAlias ); final String from = createFrom( drivingTable, drivingAlias ); //now render the joins JoinFragment jf = createJoin( tableNumbers, drivingAlias ); //now render the select clause SelectFragment selectFragment = createSelect( columnNumbers, formulaNumbers ); //now tie it all together Select select = new Select( getFactory().getDialect() ); select.setSelectClause( selectFragment.toFragmentString().substring( 2 ) ); select.setFromClause( from ); select.setWhereClause( where ); select.setOuterJoins( jf.toFromFragmentString(), jf.toWhereFragmentString() ); if ( getFactory().getSettings().isCommentsEnabled() ) { select.setComment( "sequential select " + getEntityName() ); } return select.toStatementString(); } private String getRootAlias() { return StringHelper.generateAlias( getEntityName() ); } protected void postConstruct(Mapping mapping) throws MappingException { initPropertyPaths(mapping); //insert/update/delete SQL final int joinSpan = getTableSpan(); sqlDeleteStrings = new String[joinSpan]; sqlInsertStrings = new String[joinSpan]; sqlUpdateStrings = new String[joinSpan]; sqlLazyUpdateStrings = new String[joinSpan]; sqlUpdateByRowIdString = rowIdName == null ? null : generateUpdateString( getPropertyUpdateability(), 0, true ); sqlLazyUpdateByRowIdString = rowIdName == null ? null : generateUpdateString( getNonLazyPropertyUpdateability(), 0, true ); for ( int j = 0; j < joinSpan; j++ ) { sqlInsertStrings[j] = customSQLInsert[j] == null ? generateInsertString( getPropertyInsertability(), j ) : customSQLInsert[j]; sqlUpdateStrings[j] = customSQLUpdate[j] == null ? generateUpdateString( getPropertyUpdateability(), j, false ) : customSQLUpdate[j]; sqlLazyUpdateStrings[j] = customSQLUpdate[j] == null ? generateUpdateString( getNonLazyPropertyUpdateability(), j, false ) : customSQLUpdate[j]; sqlDeleteStrings[j] = customSQLDelete[j] == null ? generateDeleteString( j ) : customSQLDelete[j]; } tableHasColumns = new boolean[joinSpan]; for ( int j = 0; j < joinSpan; j++ ) { tableHasColumns[j] = sqlUpdateStrings[j] != null; } //select SQL sqlSnapshotSelectString = generateSnapshotSelectString(); sqlLazySelectString = generateLazySelectString(); sqlVersionSelectString = generateSelectVersionString(); if ( hasInsertGeneratedProperties() ) { sqlInsertGeneratedValuesSelectString = generateInsertGeneratedValuesSelectString(); } if ( hasUpdateGeneratedProperties() ) { sqlUpdateGeneratedValuesSelectString = generateUpdateGeneratedValuesSelectString(); } if ( isIdentifierAssignedByInsert() ) { identityDelegate = ( ( PostInsertIdentifierGenerator ) getIdentifierGenerator() ) .getInsertGeneratedIdentifierDelegate( this, getFactory().getDialect(), useGetGeneratedKeys() ); sqlIdentityInsertString = customSQLInsert[0] == null ? generateIdentityInsertString( getPropertyInsertability() ) : customSQLInsert[0]; } else { sqlIdentityInsertString = null; } logStaticSQL(); } public void postInstantiate() throws MappingException { createLoaders(); createUniqueKeyLoaders(); createQueryLoader(); } private void createLoaders() { loaders.put( LockMode.NONE, createEntityLoader( LockMode.NONE ) ); UniqueEntityLoader readLoader = createEntityLoader( LockMode.READ ); loaders.put( LockMode.READ, readLoader ); //TODO: inexact, what we really need to know is: are any outer joins used? boolean disableForUpdate = getSubclassTableSpan() > 1 && hasSubclasses() && !getFactory().getDialect().supportsOuterJoinForUpdate(); loaders.put( LockMode.UPGRADE, disableForUpdate ? readLoader : createEntityLoader( LockMode.UPGRADE ) ); loaders.put( LockMode.UPGRADE_NOWAIT, disableForUpdate ? rea