Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » persister » entity » [javadoc | source]
    1   /*
    2    * Hibernate, Relational Persistence for Idiomatic Java
    3    *
    4    * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
    5    * indicated by the @author tags or express copyright attribution
    6    * statements applied by the authors.  All third-party contributions are
    7    * distributed under license by Red Hat Middleware LLC.
    8    *
    9    * This copyrighted material is made available to anyone wishing to use, modify,
   10    * copy, or redistribute it subject to the terms and conditions of the GNU
   11    * Lesser General Public License, as published by the Free Software Foundation.
   12    *
   13    * This program is distributed in the hope that it will be useful,
   14    * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   15    * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
   16    * for more details.
   17    *
   18    * You should have received a copy of the GNU Lesser General Public License
   19    * along with this distribution; if not, write to:
   20    * Free Software Foundation, Inc.
   21    * 51 Franklin Street, Fifth Floor
   22    * Boston, MA  02110-1301  USA
   23    *
   24    */
   25   package org.hibernate.persister.entity;
   26   
   27   import java.io.Serializable;
   28   import java.sql.PreparedStatement;
   29   import java.sql.ResultSet;
   30   import java.sql.SQLException;
   31   import java.util.ArrayList;
   32   import java.util.Arrays;
   33   import java.util.HashMap;
   34   import java.util.HashSet;
   35   import java.util.Iterator;
   36   import java.util.Map;
   37   import java.util.Set;
   38   import java.util.Comparator;
   39   
   40   import org.slf4j.Logger;
   41   import org.slf4j.LoggerFactory;
   42   import org.hibernate.AssertionFailure;
   43   import org.hibernate.EntityMode;
   44   import org.hibernate.FetchMode;
   45   import org.hibernate.HibernateException;
   46   import org.hibernate.LockMode;
   47   import org.hibernate.MappingException;
   48   import org.hibernate.QueryException;
   49   import org.hibernate.StaleObjectStateException;
   50   import org.hibernate.StaleStateException;
   51   import org.hibernate.jdbc.Expectation;
   52   import org.hibernate.jdbc.Expectations;
   53   import org.hibernate.jdbc.TooManyRowsAffectedException;
   54   import org.hibernate.dialect.lock.LockingStrategy;
   55   import org.hibernate.cache.CacheConcurrencyStrategy;
   56   import org.hibernate.cache.CacheKey;
   57   import org.hibernate.cache.access.EntityRegionAccessStrategy;
   58   import org.hibernate.cache.entry.CacheEntry;
   59   import org.hibernate.cache.entry.CacheEntryStructure;
   60   import org.hibernate.cache.entry.StructuredCacheEntry;
   61   import org.hibernate.cache.entry.UnstructuredCacheEntry;
   62   import org.hibernate.engine.CascadeStyle;
   63   import org.hibernate.engine.CascadingAction;
   64   import org.hibernate.engine.EntityEntry;
   65   import org.hibernate.engine.Mapping;
   66   import org.hibernate.engine.SessionFactoryImplementor;
   67   import org.hibernate.engine.SessionImplementor;
   68   import org.hibernate.engine.Versioning;
   69   import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
   70   import org.hibernate.engine.EntityKey;
   71   import org.hibernate.engine.ValueInclusion;
   72   import org.hibernate.exception.JDBCExceptionHelper;
   73   import org.hibernate.id.IdentifierGenerator;
   74   import org.hibernate.id.PostInsertIdentifierGenerator;
   75   import org.hibernate.id.PostInsertIdentityPersister;
   76   import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
   77   import org.hibernate.id.insert.Binder;
   78   import org.hibernate.intercept.LazyPropertyInitializer;
   79   import org.hibernate.intercept.FieldInterceptionHelper;
   80   import org.hibernate.intercept.FieldInterceptor;
   81   import org.hibernate.loader.entity.BatchingEntityLoader;
   82   import org.hibernate.loader.entity.CascadeEntityLoader;
   83   import org.hibernate.loader.entity.EntityLoader;
   84   import org.hibernate.loader.entity.UniqueEntityLoader;
   85   import org.hibernate.mapping.Column;
   86   import org.hibernate.mapping.Component;
   87   import org.hibernate.mapping.PersistentClass;
   88   import org.hibernate.mapping.Property;
   89   import org.hibernate.mapping.Selectable;
   90   import org.hibernate.metadata.ClassMetadata;
   91   import org.hibernate.pretty.MessageHelper;
   92   import org.hibernate.property.BackrefPropertyAccessor;
   93   import org.hibernate.sql.Alias;
   94   import org.hibernate.sql.Delete;
   95   import org.hibernate.sql.Insert;
   96   import org.hibernate.sql.JoinFragment;
   97   import org.hibernate.sql.Select;
   98   import org.hibernate.sql.SelectFragment;
   99   import org.hibernate.sql.SimpleSelect;
  100   import org.hibernate.sql.Template;
  101   import org.hibernate.sql.Update;
  102   import org.hibernate.tuple.entity.EntityMetamodel;
  103   import org.hibernate.tuple.entity.EntityTuplizer;
  104   import org.hibernate.tuple.Tuplizer;
  105   import org.hibernate.type.AbstractComponentType;
  106   import org.hibernate.type.AssociationType;
  107   import org.hibernate.type.EntityType;
  108   import org.hibernate.type.Type;
  109   import org.hibernate.type.TypeFactory;
  110   import org.hibernate.type.VersionType;
  111   import org.hibernate.util.ArrayHelper;
  112   import org.hibernate.util.CollectionHelper;
  113   import org.hibernate.util.FilterHelper;
  114   import org.hibernate.util.StringHelper;
  115   
  116   /**
  117    * Basic functionality for persisting an entity via JDBC
  118    * through either generated or custom SQL
  119    *
  120    * @author Gavin King
  121    */
  122   public abstract class AbstractEntityPersister
  123   		implements OuterJoinLoadable, Queryable, ClassMetadata, UniqueKeyLoadable,
  124   		SQLLoadable, LazyPropertyInitializer, PostInsertIdentityPersister, Lockable {
  125   
  126   	private static final Logger log = LoggerFactory.getLogger( AbstractEntityPersister.class );
  127   
  128   	public static final String ENTITY_CLASS = "class";
  129   
  130   	// moved up from AbstractEntityPersister ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  131   	private final SessionFactoryImplementor factory;
  132   	private final EntityRegionAccessStrategy cacheAccessStrategy;
  133   	private final boolean isLazyPropertiesCacheable;
  134   	private final CacheEntryStructure cacheEntryStructure;
  135   	private final EntityMetamodel entityMetamodel;
  136   	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  137   
  138   	private final String[] rootTableKeyColumnNames;
  139   	private final String[] identifierAliases;
  140   	private final int identifierColumnSpan;
  141   	private final String versionColumnName;
  142   	private final boolean hasFormulaProperties;
  143   	private final int batchSize;
  144   	private final boolean hasSubselectLoadableCollections;
  145   	protected final String rowIdName;
  146   
  147   	private final Set lazyProperties;
  148   
  149   	// The optional SQL string defined in the where attribute
  150   	private final String sqlWhereString;
  151   	private final String sqlWhereStringTemplate;
  152   
  153   	//information about properties of this class,
  154   	//including inherited properties
  155   	//(only really needed for updatable/insertable properties)
  156   	private final int[] propertyColumnSpans;
  157   	private final String[] propertySubclassNames;
  158   	private final String[][] propertyColumnAliases;
  159   	private final String[][] propertyColumnNames;
  160   	private final String[][] propertyColumnFormulaTemplates;
  161   	private final boolean[][] propertyColumnUpdateable;
  162   	private final boolean[][] propertyColumnInsertable;
  163   	private final boolean[] propertyUniqueness;
  164   	private final boolean[] propertySelectable;
  165   
  166   	//information about lazy properties of this class
  167   	private final String[] lazyPropertyNames;
  168   	private final int[] lazyPropertyNumbers;
  169   	private final Type[] lazyPropertyTypes;
  170   	private final String[][] lazyPropertyColumnAliases;
  171   
  172   	//information about all properties in class hierarchy
  173   	private final String[] subclassPropertyNameClosure;
  174   	private final String[] subclassPropertySubclassNameClosure;
  175   	private final Type[] subclassPropertyTypeClosure;
  176   	private final String[][] subclassPropertyFormulaTemplateClosure;
  177   	private final String[][] subclassPropertyColumnNameClosure;
  178   	private final FetchMode[] subclassPropertyFetchModeClosure;
  179   	private final boolean[] subclassPropertyNullabilityClosure;
  180   	private final boolean[] propertyDefinedOnSubclass;
  181   	private final int[][] subclassPropertyColumnNumberClosure;
  182   	private final int[][] subclassPropertyFormulaNumberClosure;
  183   	private final CascadeStyle[] subclassPropertyCascadeStyleClosure;
  184   
  185   	//information about all columns/formulas in class hierarchy
  186   	private final String[] subclassColumnClosure;
  187   	private final boolean[] subclassColumnLazyClosure;
  188   	private final String[] subclassColumnAliasClosure;
  189   	private final boolean[] subclassColumnSelectableClosure;
  190   	private final String[] subclassFormulaClosure;
  191   	private final String[] subclassFormulaTemplateClosure;
  192   	private final String[] subclassFormulaAliasClosure;
  193   	private final boolean[] subclassFormulaLazyClosure;
  194   
  195   	// dynamic filters attached to the class-level
  196   	private final FilterHelper filterHelper;
  197   
  198   	private final Map uniqueKeyLoaders = new HashMap();
  199   	private final Map lockers = new HashMap();
  200   	private final Map loaders = new HashMap();
  201   
  202   	// SQL strings
  203   	private String sqlVersionSelectString;
  204   	private String sqlSnapshotSelectString;
  205   	private String sqlLazySelectString;
  206   
  207   	private String sqlIdentityInsertString;
  208   	private String sqlUpdateByRowIdString;
  209   	private String sqlLazyUpdateByRowIdString;
  210   
  211   	private String[] sqlDeleteStrings;
  212   	private String[] sqlInsertStrings;
  213   	private String[] sqlUpdateStrings;
  214   	private String[] sqlLazyUpdateStrings;
  215   
  216   	private String sqlInsertGeneratedValuesSelectString;
  217   	private String sqlUpdateGeneratedValuesSelectString;
  218   
  219   	//Custom SQL (would be better if these were private)
  220   	protected boolean[] insertCallable;
  221   	protected boolean[] updateCallable;
  222   	protected boolean[] deleteCallable;
  223   	protected String[] customSQLInsert;
  224   	protected String[] customSQLUpdate;
  225   	protected String[] customSQLDelete;
  226   	protected ExecuteUpdateResultCheckStyle[] insertResultCheckStyles;
  227   	protected ExecuteUpdateResultCheckStyle[] updateResultCheckStyles;
  228   	protected ExecuteUpdateResultCheckStyle[] deleteResultCheckStyles;
  229   
  230   	private InsertGeneratedIdentifierDelegate identityDelegate;
  231   
  232   	private boolean[] tableHasColumns;
  233   
  234   	private final String loaderName;
  235   
  236   	private UniqueEntityLoader queryLoader;
  237   
  238   	private final String temporaryIdTableName;
  239   	private final String temporaryIdTableDDL;
  240   
  241   	private final Map subclassPropertyAliases = new HashMap();
  242   	private final Map subclassPropertyColumnNames = new HashMap();
  243   
  244   	protected final BasicEntityPropertyMapping propertyMapping;
  245   
  246   	protected void addDiscriminatorToInsert(Insert insert) {}
  247   
  248   	protected void addDiscriminatorToSelect(SelectFragment select, String name, String suffix) {}
  249   
  250   	protected abstract int[] getSubclassColumnTableNumberClosure();
  251   
  252   	protected abstract int[] getSubclassFormulaTableNumberClosure();
  253   
  254   	public abstract String getSubclassTableName(int j);
  255   
  256   	protected abstract String[] getSubclassTableKeyColumns(int j);
  257   
  258   	protected abstract boolean isClassOrSuperclassTable(int j);
  259   
  260   	protected abstract int getSubclassTableSpan();
  261   
  262   	protected abstract int getTableSpan();
  263   
  264   	protected abstract boolean isTableCascadeDeleteEnabled(int j);
  265   
  266   	protected abstract String getTableName(int j);
  267   
  268   	protected abstract String[] getKeyColumns(int j);
  269   
  270   	protected abstract boolean isPropertyOfTable(int property, int j);
  271   
  272   	protected abstract int[] getPropertyTableNumbersInSelect();
  273   
  274   	protected abstract int[] getPropertyTableNumbers();
  275   
  276   	protected abstract int getSubclassPropertyTableNumber(int i);
  277   
  278   	protected abstract String filterFragment(String alias) throws MappingException;
  279   
  280   	private static final String DISCRIMINATOR_ALIAS = "clazz_";
  281   
  282   	public String getDiscriminatorColumnName() {
  283   		return DISCRIMINATOR_ALIAS;
  284   	}
  285   
  286   	protected String getDiscriminatorAlias() {
  287   		return DISCRIMINATOR_ALIAS;
  288   	}
  289   
  290   	protected String getDiscriminatorFormulaTemplate() {
  291   		return null;
  292   	}
  293   
  294   	protected boolean isInverseTable(int j) {
  295   		return false;
  296   	}
  297   
  298   	protected boolean isNullableTable(int j) {
  299   		return false;
  300   	}
  301   
  302   	protected boolean isNullableSubclassTable(int j) {
  303   		return false;
  304   	}
  305   
  306   	protected boolean isInverseSubclassTable(int j) {
  307   		return false;
  308   	}
  309   
  310   	public boolean isSubclassEntityName(String entityName) {
  311   		return entityMetamodel.getSubclassEntityNames().contains(entityName);
  312   	}
  313   
  314   	private boolean[] getTableHasColumns() {
  315   		return tableHasColumns;
  316   	}
  317   
  318   	public String[] getRootTableKeyColumnNames() {
  319   		return rootTableKeyColumnNames;
  320   	}
  321   
  322   	protected String[] getSQLUpdateByRowIdStrings() {
  323   		if ( sqlUpdateByRowIdString == null ) {
  324   			throw new AssertionFailure( "no update by row id" );
  325   		}
  326   		String[] result = new String[getTableSpan() + 1];
  327   		result[0] = sqlUpdateByRowIdString;
  328   		System.arraycopy( sqlUpdateStrings, 0, result, 1, getTableSpan() );
  329   		return result;
  330   	}
  331   
  332   	protected String[] getSQLLazyUpdateByRowIdStrings() {
  333   		if ( sqlLazyUpdateByRowIdString == null ) {
  334   			throw new AssertionFailure( "no update by row id" );
  335   		}
  336   		String[] result = new String[getTableSpan()];
  337   		result[0] = sqlLazyUpdateByRowIdString;
  338   		for ( int i = 1; i < getTableSpan(); i++ ) {
  339   			result[i] = sqlLazyUpdateStrings[i];
  340   		}
  341   		return result;
  342   	}
  343   
  344   	protected String getSQLSnapshotSelectString() {
  345   		return sqlSnapshotSelectString;
  346   	}
  347   
  348   	protected String getSQLLazySelectString() {
  349   		return sqlLazySelectString;
  350   	}
  351   
  352   	protected String[] getSQLDeleteStrings() {
  353   		return sqlDeleteStrings;
  354   	}
  355   
  356   	protected String[] getSQLInsertStrings() {
  357   		return sqlInsertStrings;
  358   	}
  359   
  360   	protected String[] getSQLUpdateStrings() {
  361   		return sqlUpdateStrings;
  362   	}
  363   
  364   	protected String[] getSQLLazyUpdateStrings() {
  365   		return sqlLazyUpdateStrings;
  366   	}
  367   
  368   	/**
  369   	 * The query that inserts a row, letting the database generate an id
  370   	 * 
  371   	 * @return The IDENTITY-based insertion query.
  372   	 */
  373   	protected String getSQLIdentityInsertString() {
  374   		return sqlIdentityInsertString;
  375   	}
  376   
  377   	protected String getVersionSelectString() {
  378   		return sqlVersionSelectString;
  379   	}
  380   
  381   	protected boolean isInsertCallable(int j) {
  382   		return insertCallable[j];
  383   	}
  384   
  385   	protected boolean isUpdateCallable(int j) {
  386   		return updateCallable[j];
  387   	}
  388   
  389   	protected boolean isDeleteCallable(int j) {
  390   		return deleteCallable[j];
  391   	}
  392   
  393   	protected boolean isSubclassPropertyDeferred(String propertyName, String entityName) {
  394   		return false;
  395   	}
  396   
  397   	protected boolean isSubclassTableSequentialSelect(int j) {
  398   		return false;
  399   	}
  400   
  401   	public boolean hasSequentialSelect() {
  402   		return false;
  403   	}
  404   
  405   	/**
  406   	 * Decide which tables need to be updated.
  407   	 * <p/>
  408   	 * The return here is an array of boolean values with each index corresponding
  409   	 * to a given table in the scope of this persister.
  410   	 *
  411   	 * @param dirtyProperties The indices of all the entity properties considered dirty.
  412   	 * @param hasDirtyCollection Whether any collections owned by the entity which were considered dirty.
  413   	 *
  414   	 * @return Array of booleans indicating which table require updating.
  415   	 */
  416   	protected boolean[] getTableUpdateNeeded(final int[] dirtyProperties, boolean hasDirtyCollection) {
  417   
  418   		if ( dirtyProperties == null ) {
  419   			return getTableHasColumns(); // for objects that came in via update()
  420   		}
  421   		else {
  422   			boolean[] updateability = getPropertyUpdateability();
  423   			int[] propertyTableNumbers = getPropertyTableNumbers();
  424   			boolean[] tableUpdateNeeded = new boolean[ getTableSpan() ];
  425   			for ( int i = 0; i < dirtyProperties.length; i++ ) {
  426   				int property = dirtyProperties[i];
  427   				int table = propertyTableNumbers[property];
  428   				tableUpdateNeeded[table] = tableUpdateNeeded[table] ||
  429   						( getPropertyColumnSpan(property) > 0 && updateability[property] );
  430   			}
  431   			if ( isVersioned() ) {
  432   				tableUpdateNeeded[0] = tableUpdateNeeded[0] ||
  433   					Versioning.isVersionIncrementRequired( dirtyProperties, hasDirtyCollection, getPropertyVersionability() );
  434   			}
  435   			return tableUpdateNeeded;
  436   		}
  437   	}
  438   
  439   	public boolean hasRowId() {
  440   		return rowIdName != null;
  441   	}
  442   
  443   	public AbstractEntityPersister(
  444   			final PersistentClass persistentClass,
  445   			final EntityRegionAccessStrategy cacheAccessStrategy,
  446   			final SessionFactoryImplementor factory) throws HibernateException {
  447   
  448   		// moved up from AbstractEntityPersister ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  449   		this.factory = factory;
  450   		this.cacheAccessStrategy = cacheAccessStrategy;
  451   		isLazyPropertiesCacheable = persistentClass.isLazyPropertiesCacheable();
  452   		this.cacheEntryStructure = factory.getSettings().isStructuredCacheEntriesEnabled() ?
  453   				(CacheEntryStructure) new StructuredCacheEntry(this) :
  454   				(CacheEntryStructure) new UnstructuredCacheEntry();
  455   
  456   		this.entityMetamodel = new EntityMetamodel( persistentClass, factory );
  457   		// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  458   
  459   		int batch = persistentClass.getBatchSize();
  460   		if ( batch == -1 ) {
  461   			batch = factory.getSettings().getDefaultBatchFetchSize();
  462   		}
  463   		batchSize = batch;
  464   		hasSubselectLoadableCollections = persistentClass.hasSubselectLoadableCollections();
  465   
  466   		propertyMapping = new BasicEntityPropertyMapping( this );
  467   
  468   		// IDENTIFIER
  469   
  470   		identifierColumnSpan = persistentClass.getIdentifier().getColumnSpan();
  471   		rootTableKeyColumnNames = new String[identifierColumnSpan];
  472   		identifierAliases = new String[identifierColumnSpan];
  473   
  474   		rowIdName = persistentClass.getRootTable().getRowId();
  475   
  476   		loaderName = persistentClass.getLoaderName();
  477   
  478   		Iterator iter = persistentClass.getIdentifier().getColumnIterator();
  479   		int i = 0;
  480   		while ( iter.hasNext() ) {
  481   			Column col = ( Column ) iter.next();
  482   			rootTableKeyColumnNames[i] = col.getQuotedName( factory.getDialect() );
  483   			identifierAliases[i] = col.getAlias( factory.getDialect(), persistentClass.getRootTable() );
  484   			i++;
  485   		}
  486   
  487   		// VERSION
  488   
  489   		if ( persistentClass.isVersioned() ) {
  490   			versionColumnName = ( ( Column ) persistentClass.getVersion().getColumnIterator().next() ).getQuotedName( factory.getDialect() );
  491   		}
  492   		else {
  493   			versionColumnName = null;
  494   		}
  495   
  496   		//WHERE STRING
  497   
  498   		sqlWhereString = StringHelper.isNotEmpty( persistentClass.getWhere() ) ? "( " + persistentClass.getWhere() + ") " : null;
  499   		sqlWhereStringTemplate = sqlWhereString == null ?
  500   				null :
  501   				Template.renderWhereStringTemplate( sqlWhereString, factory.getDialect(), factory.getSqlFunctionRegistry() );
  502   
  503   		// PROPERTIES
  504   
  505   		final boolean lazyAvailable = isInstrumented(EntityMode.POJO);
  506   
  507   		int hydrateSpan = entityMetamodel.getPropertySpan();
  508   		propertyColumnSpans = new int[hydrateSpan];
  509   		propertySubclassNames = new String[hydrateSpan];
  510   		propertyColumnAliases = new String[hydrateSpan][];
  511   		propertyColumnNames = new String[hydrateSpan][];
  512   		propertyColumnFormulaTemplates = new String[hydrateSpan][];
  513   		propertyUniqueness = new boolean[hydrateSpan];
  514   		propertySelectable = new boolean[hydrateSpan];
  515   		propertyColumnUpdateable = new boolean[hydrateSpan][];
  516   		propertyColumnInsertable = new boolean[hydrateSpan][];
  517   		HashSet thisClassProperties = new HashSet();
  518   
  519   		lazyProperties = new HashSet();
  520   		ArrayList lazyNames = new ArrayList();
  521   		ArrayList lazyNumbers = new ArrayList();
  522   		ArrayList lazyTypes = new ArrayList();
  523   		ArrayList lazyColAliases = new ArrayList();
  524   
  525   		iter = persistentClass.getPropertyClosureIterator();
  526   		i = 0;
  527   		boolean foundFormula = false;
  528   		while ( iter.hasNext() ) {
  529   			Property prop = ( Property ) iter.next();
  530   			thisClassProperties.add( prop );
  531   
  532   			int span = prop.getColumnSpan();
  533   			propertyColumnSpans[i] = span;
  534   			propertySubclassNames[i] = prop.getPersistentClass().getEntityName();
  535   			String[] colNames = new String[span];
  536   			String[] colAliases = new String[span];
  537   			String[] templates = new String[span];
  538   			Iterator colIter = prop.getColumnIterator();
  539   			int k = 0;
  540   			while ( colIter.hasNext() ) {
  541   				Selectable thing = ( Selectable ) colIter.next();
  542   				colAliases[k] = thing.getAlias( factory.getDialect() , prop.getValue().getTable() );
  543   				if ( thing.isFormula() ) {
  544   					foundFormula = true;
  545   					templates[k] = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
  546   				}
  547   				else {
  548   					colNames[k] = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
  549   				}
  550   				k++;
  551   			}
  552   			propertyColumnNames[i] = colNames;
  553   			propertyColumnFormulaTemplates[i] = templates;
  554   			propertyColumnAliases[i] = colAliases;
  555   
  556   			if ( lazyAvailable && prop.isLazy() ) {
  557   				lazyProperties.add( prop.getName() );
  558   				lazyNames.add( prop.getName() );
  559   				lazyNumbers.add( new Integer( i ) );
  560   				lazyTypes.add( prop.getValue().getType() );
  561   				lazyColAliases.add( colAliases );
  562   			}
  563   
  564   			propertyColumnUpdateable[i] = prop.getValue().getColumnUpdateability();
  565   			propertyColumnInsertable[i] = prop.getValue().getColumnInsertability();
  566   
  567   			propertySelectable[i] = prop.isSelectable();
  568   
  569   			propertyUniqueness[i] = prop.getValue().isAlternateUniqueKey();
  570   
  571   			i++;
  572   
  573   		}
  574   		hasFormulaProperties = foundFormula;
  575   		lazyPropertyColumnAliases = ArrayHelper.to2DStringArray( lazyColAliases );
  576   		lazyPropertyNames = ArrayHelper.toStringArray( lazyNames );
  577   		lazyPropertyNumbers = ArrayHelper.toIntArray( lazyNumbers );
  578   		lazyPropertyTypes = ArrayHelper.toTypeArray( lazyTypes );
  579   
  580   		// SUBCLASS PROPERTY CLOSURE
  581   
  582   		ArrayList columns = new ArrayList();
  583   		ArrayList columnsLazy = new ArrayList();
  584   		ArrayList aliases = new ArrayList();
  585   		ArrayList formulas = new ArrayList();
  586   		ArrayList formulaAliases = new ArrayList();
  587   		ArrayList formulaTemplates = new ArrayList();
  588   		ArrayList formulasLazy = new ArrayList();
  589   		ArrayList types = new ArrayList();
  590   		ArrayList names = new ArrayList();
  591   		ArrayList classes = new ArrayList();
  592   		ArrayList templates = new ArrayList();
  593   		ArrayList propColumns = new ArrayList();
  594   		ArrayList joinedFetchesList = new ArrayList();
  595   		ArrayList cascades = new ArrayList();
  596   		ArrayList definedBySubclass = new ArrayList();
  597   		ArrayList propColumnNumbers = new ArrayList();
  598   		ArrayList propFormulaNumbers = new ArrayList();
  599   		ArrayList columnSelectables = new ArrayList();
  600   		ArrayList propNullables = new ArrayList();
  601   
  602   		iter = persistentClass.getSubclassPropertyClosureIterator();
  603   		while ( iter.hasNext() ) {
  604   			Property prop = ( Property ) iter.next();
  605   			names.add( prop.getName() );
  606   			classes.add( prop.getPersistentClass().getEntityName() );
  607   			boolean isDefinedBySubclass = !thisClassProperties.contains( prop );
  608   			definedBySubclass.add( Boolean.valueOf( isDefinedBySubclass ) );
  609   			propNullables.add( Boolean.valueOf( prop.isOptional() || isDefinedBySubclass ) ); //TODO: is this completely correct?
  610   			types.add( prop.getType() );
  611   
  612   			Iterator colIter = prop.getColumnIterator();
  613   			String[] cols = new String[prop.getColumnSpan()];
  614   			String[] forms = new String[prop.getColumnSpan()];
  615   			int[] colnos = new int[prop.getColumnSpan()];
  616   			int[] formnos = new int[prop.getColumnSpan()];
  617   			int l = 0;
  618   			Boolean lazy = Boolean.valueOf( prop.isLazy() && lazyAvailable );
  619   			while ( colIter.hasNext() ) {
  620   				Selectable thing = ( Selectable ) colIter.next();
  621   				if ( thing.isFormula() ) {
  622   					String template = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
  623   					formnos[l] = formulaTemplates.size();
  624   					colnos[l] = -1;
  625   					formulaTemplates.add( template );
  626   					forms[l] = template;
  627   					formulas.add( thing.getText( factory.getDialect() ) );
  628   					formulaAliases.add( thing.getAlias( factory.getDialect() ) );
  629   					formulasLazy.add( lazy );
  630   				}
  631   				else {
  632   					String colName = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
  633   					colnos[l] = columns.size(); //before add :-)
  634   					formnos[l] = -1;
  635   					columns.add( colName );
  636   					cols[l] = colName;
  637   					aliases.add( thing.getAlias( factory.getDialect(), prop.getValue().getTable() ) );
  638   					columnsLazy.add( lazy );
  639   					columnSelectables.add( Boolean.valueOf( prop.isSelectable() ) );
  640   				}
  641   				l++;
  642   			}
  643   			propColumns.add( cols );
  644   			templates.add( forms );
  645   			propColumnNumbers.add( colnos );
  646   			propFormulaNumbers.add( formnos );
  647   
  648   			joinedFetchesList.add( prop.getValue().getFetchMode() );
  649   			cascades.add( prop.getCascadeStyle() );
  650   		}
  651   		subclassColumnClosure = ArrayHelper.toStringArray( columns );
  652   		subclassColumnAliasClosure = ArrayHelper.toStringArray( aliases );
  653   		subclassColumnLazyClosure = ArrayHelper.toBooleanArray( columnsLazy );
  654   		subclassColumnSelectableClosure = ArrayHelper.toBooleanArray( columnSelectables );
  655   
  656   		subclassFormulaClosure = ArrayHelper.toStringArray( formulas );
  657   		subclassFormulaTemplateClosure = ArrayHelper.toStringArray( formulaTemplates );
  658   		subclassFormulaAliasClosure = ArrayHelper.toStringArray( formulaAliases );
  659   		subclassFormulaLazyClosure = ArrayHelper.toBooleanArray( formulasLazy );
  660   
  661   		subclassPropertyNameClosure = ArrayHelper.toStringArray( names );
  662   		subclassPropertySubclassNameClosure = ArrayHelper.toStringArray( classes );
  663   		subclassPropertyTypeClosure = ArrayHelper.toTypeArray( types );
  664   		subclassPropertyNullabilityClosure = ArrayHelper.toBooleanArray( propNullables );
  665   		subclassPropertyFormulaTemplateClosure = ArrayHelper.to2DStringArray( templates );
  666   		subclassPropertyColumnNameClosure = ArrayHelper.to2DStringArray( propColumns );
  667   		subclassPropertyColumnNumberClosure = ArrayHelper.to2DIntArray( propColumnNumbers );
  668   		subclassPropertyFormulaNumberClosure = ArrayHelper.to2DIntArray( propFormulaNumbers );
  669   
  670   		subclassPropertyCascadeStyleClosure = new CascadeStyle[cascades.size()];
  671   		iter = cascades.iterator();
  672   		int j = 0;
  673   		while ( iter.hasNext() ) {
  674   			subclassPropertyCascadeStyleClosure[j++] = ( CascadeStyle ) iter.next();
  675   		}
  676   		subclassPropertyFetchModeClosure = new FetchMode[joinedFetchesList.size()];
  677   		iter = joinedFetchesList.iterator();
  678   		j = 0;
  679   		while ( iter.hasNext() ) {
  680   			subclassPropertyFetchModeClosure[j++] = ( FetchMode ) iter.next();
  681   		}
  682   
  683   		propertyDefinedOnSubclass = new boolean[definedBySubclass.size()];
  684   		iter = definedBySubclass.iterator();
  685   		j = 0;
  686   		while ( iter.hasNext() ) {
  687   			propertyDefinedOnSubclass[j++] = ( ( Boolean ) iter.next() ).booleanValue();
  688   		}
  689   
  690   		// Handle any filters applied to the class level
  691   		filterHelper = new FilterHelper( persistentClass.getFilterMap(), factory.getDialect(), factory.getSqlFunctionRegistry() );
  692   
  693   		temporaryIdTableName = persistentClass.getTemporaryIdTableName();
  694   		temporaryIdTableDDL = persistentClass.getTemporaryIdTableDDL();
  695   	}
  696   
  697   	protected String generateLazySelectString() {
  698   
  699   		if ( !entityMetamodel.hasLazyProperties() ) {
  700   			return null;
  701   		}
  702   
  703   		HashSet tableNumbers = new HashSet();
  704   		ArrayList columnNumbers = new ArrayList();
  705   		ArrayList formulaNumbers = new ArrayList();
  706   		for ( int i = 0; i < lazyPropertyNames.length; i++ ) {
  707   			// all this only really needs to consider properties
  708   			// of this class, not its subclasses, but since we
  709   			// are reusing code used for sequential selects, we
  710   			// use the subclass closure
  711   			int propertyNumber = getSubclassPropertyIndex( lazyPropertyNames[i] );
  712   
  713   			int tableNumber = getSubclassPropertyTableNumber( propertyNumber );
  714   			tableNumbers.add( new Integer( tableNumber ) );
  715   
  716   			int[] colNumbers = subclassPropertyColumnNumberClosure[propertyNumber];
  717   			for ( int j = 0; j < colNumbers.length; j++ ) {
  718   				if ( colNumbers[j]!=-1 ) {
  719   					columnNumbers.add( new Integer( colNumbers[j] ) );
  720   				}
  721   			}
  722   			int[] formNumbers = subclassPropertyFormulaNumberClosure[propertyNumber];
  723   			for ( int j = 0; j < formNumbers.length; j++ ) {
  724   				if ( formNumbers[j]!=-1 ) {
  725   					formulaNumbers.add( new Integer( formNumbers[j] ) );
  726   				}
  727   			}
  728   		}
  729   
  730   		if ( columnNumbers.size()==0 && formulaNumbers.size()==0 ) {
  731   			// only one-to-one is lazy fetched
  732   			return null;
  733   		}
  734   
  735   		return renderSelect( ArrayHelper.toIntArray( tableNumbers ),
  736   				ArrayHelper.toIntArray( columnNumbers ),
  737   				ArrayHelper.toIntArray( formulaNumbers ) );
  738   
  739   	}
  740   
  741   	public Object initializeLazyProperty(String fieldName, Object entity, SessionImplementor session)
  742   			throws HibernateException {
  743   
  744   		final Serializable id = session.getContextEntityIdentifier( entity );
  745   
  746   		final EntityEntry entry = session.getPersistenceContext().getEntry( entity );
  747   		if ( entry == null ) {
  748   			throw new HibernateException( "entity is not associated with the session: " + id );
  749   		}
  750   
  751   		if ( log.isTraceEnabled() ) {
  752   			log.trace(
  753   					"initializing lazy properties of: " +
  754   					MessageHelper.infoString( this, id, getFactory() ) +
  755   					", field access: " + fieldName
  756   				);
  757   		}
  758   
  759   		if ( hasCache() ) {
  760   			CacheKey cacheKey = new CacheKey(id, getIdentifierType(), getEntityName(), session.getEntityMode(), getFactory() );
  761   			Object ce = getCacheAccessStrategy().get( cacheKey, session.getTimestamp() );
  762   			if (ce!=null) {
  763   				CacheEntry cacheEntry = (CacheEntry) getCacheEntryStructure().destructure(ce, factory);
  764   				if ( !cacheEntry.areLazyPropertiesUnfetched() ) {
  765   					//note early exit here:
  766   					return initializeLazyPropertiesFromCache( fieldName, entity, session, entry, cacheEntry );
  767   				}
  768   			}
  769   		}
  770   
  771   		return initializeLazyPropertiesFromDatastore( fieldName, entity, session, id, entry );
  772   
  773   	}
  774   
  775   	private Object initializeLazyPropertiesFromDatastore(
  776   			final String fieldName,
  777   			final Object entity,
  778   			final SessionImplementor session,
  779   			final Serializable id,
  780   			final EntityEntry entry) {
  781   
  782   		if ( !hasLazyProperties() ) {
  783   			throw new AssertionFailure("no lazy properties");
  784   		}
  785   
  786   		log.trace("initializing lazy properties from datastore");
  787   
  788   		try {
  789   
  790   			Object result = null;
  791   			PreparedStatement ps = null;
  792   			try {
  793   				final String lazySelect = getSQLLazySelectString();
  794   				ResultSet rs = null;
  795   				try {
  796   					if ( lazySelect != null ) {
  797   						// null sql means that the only lazy properties
  798   						// are shared PK one-to-one associations which are
  799   						// handled differently in the Type#nullSafeGet code...
  800   						ps = session.getBatcher().prepareSelectStatement(lazySelect);
  801   						getIdentifierType().nullSafeSet( ps, id, 1, session );
  802   						rs = ps.executeQuery();
  803   						rs.next();
  804   					}
  805   					final Object[] snapshot = entry.getLoadedState();
  806   					for ( int j = 0; j < lazyPropertyNames.length; j++ ) {
  807   						Object propValue = lazyPropertyTypes[j].nullSafeGet( rs, lazyPropertyColumnAliases[j], session, entity );
  808   						if ( initializeLazyProperty( fieldName, entity, session, snapshot, j, propValue ) ) {
  809   							result = propValue;
  810   						}
  811   					}
  812   				}
  813   				finally {
  814   					if ( rs != null ) {
  815   						rs.close();
  816   					}
  817   				}
  818   			}
  819   			finally {
  820   				if ( ps != null ) {
  821   					session.getBatcher().closeStatement( ps );
  822   				}
  823   			}
  824   
  825   			log.trace( "done initializing lazy properties" );
  826   
  827   			return result;
  828   
  829   		}
  830   		catch ( SQLException sqle ) {
  831   			throw JDBCExceptionHelper.convert(
  832   					getFactory().getSQLExceptionConverter(),
  833   					sqle,
  834   					"could not initialize lazy properties: " +
  835   					MessageHelper.infoString( this, id, getFactory() ),
  836   					getSQLLazySelectString()
  837   				);
  838   		}
  839   	}
  840   
  841   	private Object initializeLazyPropertiesFromCache(
  842   			final String fieldName,
  843   			final Object entity,
  844   			final SessionImplementor session,
  845   			final EntityEntry entry,
  846   			final CacheEntry cacheEntry
  847   	) {
  848   
  849   		log.trace("initializing lazy properties from second-level cache");
  850   
  851   		Object result = null;
  852   		Serializable[] disassembledValues = cacheEntry.getDisassembledState();
  853   		final Object[] snapshot = entry.getLoadedState();
  854   		for ( int j = 0; j < lazyPropertyNames.length; j++ ) {
  855   			final Object propValue = lazyPropertyTypes[j].assemble(
  856   					disassembledValues[ lazyPropertyNumbers[j] ],
  857   					session,
  858   					entity
  859   				);
  860   			if ( initializeLazyProperty( fieldName, entity, session, snapshot, j, propValue ) ) {
  861   				result = propValue;
  862   			}
  863   		}
  864   
  865   		log.trace( "done initializing lazy properties" );
  866   
  867   		return result;
  868   	}
  869   
  870   	private boolean initializeLazyProperty(
  871   			final String fieldName,
  872   			final Object entity,
  873   			final SessionImplementor session,
  874   			final Object[] snapshot,
  875   			final int j,
  876   			final Object propValue) {
  877   		setPropertyValue( entity, lazyPropertyNumbers[j], propValue, session.getEntityMode() );
  878   		if (snapshot != null) {
  879   			// object have been loaded with setReadOnly(true); HHH-2236
  880   			snapshot[ lazyPropertyNumbers[j] ] = lazyPropertyTypes[j].deepCopy( propValue, session.getEntityMode(), factory );
  881   		}
  882   		return fieldName.equals( lazyPropertyNames[j] );
  883   	}
  884   
  885   	public boolean isBatchable() {
  886   		return optimisticLockMode()==Versioning.OPTIMISTIC_LOCK_NONE ||
  887   			( !isVersioned() && optimisticLockMode()==Versioning.OPTIMISTIC_LOCK_VERSION ) ||
  888   			getFactory().getSettings().isJdbcBatchVersionedData();
  889   	}
  890   
  891   	public Serializable[] getQuerySpaces() {
  892   		return getPropertySpaces();
  893   	}
  894   
  895   	protected Set getLazyProperties() {
  896   		return lazyProperties;
  897   	}
  898   
  899   	public boolean isBatchLoadable() {
  900   		return batchSize > 1;
  901   	}
  902   
  903   	public String[] getIdentifierColumnNames() {
  904   		return rootTableKeyColumnNames;
  905   	}
  906   
  907   	protected int getIdentifierColumnSpan() {
  908   		return identifierColumnSpan;
  909   	}
  910   
  911   	protected String[] getIdentifierAliases() {
  912   		return identifierAliases;
  913   	}
  914   
  915   	public String getVersionColumnName() {
  916   		return versionColumnName;
  917   	}
  918   
  919   	protected String getVersionedTableName() {
  920   		return getTableName( 0 );
  921   	}
  922   
  923   	protected boolean[] getSubclassColumnLazyiness() {
  924   		return subclassColumnLazyClosure;
  925   	}
  926   
  927   	protected boolean[] getSubclassFormulaLazyiness() {
  928   		return subclassFormulaLazyClosure;
  929   	}
  930   
  931   	/**
  932   	 * We can't immediately add to the cache if we have formulas
  933   	 * which must be evaluated, or if we have the possibility of
  934   	 * two concurrent updates to the same item being merged on
  935   	 * the database. This can happen if (a) the item is not
  936   	 * versioned and either (b) we have dynamic update enabled
  937   	 * or (c) we have multiple tables holding the state of the
  938   	 * item.
  939   	 */
  940   	public boolean isCacheInvalidationRequired() {
  941   		return hasFormulaProperties() ||
  942   				( !isVersioned() && ( entityMetamodel.isDynamicUpdate() || getTableSpan() > 1 ) );
  943   	}
  944   
  945   	public boolean isLazyPropertiesCacheable() {
  946   		return isLazyPropertiesCacheable;
  947   	}
  948   
  949   	public String selectFragment(String alias, String suffix) {
  950   		return identifierSelectFragment( alias, suffix ) +
  951   				propertySelectFragment( alias, suffix, false );
  952   	}
  953   
  954   	public String[] getIdentifierAliases(String suffix) {
  955   		// NOTE: this assumes something about how propertySelectFragment is implemented by the subclass!
  956   		// was toUnqotedAliasStrings( getIdentiferColumnNames() ) before - now tried
  957   		// to remove that unqoting and missing aliases..
  958   		return new Alias( suffix ).toAliasStrings( getIdentifierAliases() );
  959   	}
  960   
  961   	public String[] getPropertyAliases(String suffix, int i) {
  962   		// NOTE: this assumes something about how propertySelectFragment is implemented by the subclass!
  963   		return new Alias( suffix ).toUnquotedAliasStrings( propertyColumnAliases[i] );
  964   	}
  965   
  966   	public String getDiscriminatorAlias(String suffix) {
  967   		// NOTE: this assumes something about how propertySelectFragment is implemented by the subclass!
  968   		// was toUnqotedAliasStrings( getdiscriminatorColumnName() ) before - now tried
  969   		// to remove that unqoting and missing aliases..
  970   		return entityMetamodel.hasSubclasses() ?
  971   				new Alias( suffix ).toAliasString( getDiscriminatorAlias() ) :
  972   				null;
  973   	}
  974   
  975   	public String identifierSelectFragment(String name, String suffix) {
  976   		return new SelectFragment()
  977   				.setSuffix( suffix )
  978   				.addColumns( name, getIdentifierColumnNames(), getIdentifierAliases() )
  979   				.toFragmentString()
  980   				.substring( 2 ); //strip leading ", "
  981   	}
  982   
  983   
  984   	public String propertySelectFragment(String name, String suffix, boolean allProperties) {
  985   
  986   		SelectFragment select = new SelectFragment()
  987   				.setSuffix( suffix )
  988   				.setUsedAliases( getIdentifierAliases() );
  989   
  990   		int[] columnTableNumbers = getSubclassColumnTableNumberClosure();
  991   		String[] columnAliases = getSubclassColumnAliasClosure();
  992   		String[] columns = getSubclassColumnClosure();
  993   		for ( int i = 0; i < getSubclassColumnClosure().length; i++ ) {
  994   			boolean selectable = ( allProperties || !subclassColumnLazyClosure[i] ) &&
  995   				!isSubclassTableSequentialSelect( columnTableNumbers[i] ) &&
  996   				subclassColumnSelectableClosure[i];
  997   			if ( selectable ) {
  998   				String subalias = generateTableAlias( name, columnTableNumbers[i] );
  999   				select.addColumn( subalias, columns[i], columnAliases[i] );
 1000   			}
 1001   		}
 1002   
 1003   		int[] formulaTableNumbers = getSubclassFormulaTableNumberClosure();
 1004   		String[] formulaTemplates = getSubclassFormulaTemplateClosure();
 1005   		String[] formulaAliases = getSubclassFormulaAliasClosure();
 1006   		for ( int i = 0; i < getSubclassFormulaTemplateClosure().length; i++ ) {
 1007   			boolean selectable = ( allProperties || !subclassFormulaLazyClosure[i] )
 1008   				&& !isSubclassTableSequentialSelect( formulaTableNumbers[i] );
 1009   			if ( selectable ) {
 1010   				String subalias = generateTableAlias( name, formulaTableNumbers[i] );
 1011   				select.addFormula( subalias, formulaTemplates[i], formulaAliases[i] );
 1012   			}
 1013   		}
 1014   
 1015   		if ( entityMetamodel.hasSubclasses() ) {
 1016   			addDiscriminatorToSelect( select, name, suffix );
 1017   		}
 1018   
 1019   		if ( hasRowId() ) {
 1020   			select.addColumn( name, rowIdName, ROWID_ALIAS );
 1021   		}
 1022   
 1023   		return select.toFragmentString();
 1024   	}
 1025   
 1026   	public Object[] getDatabaseSnapshot(Serializable id, SessionImplementor session)
 1027   			throws HibernateException {
 1028   
 1029   		if ( log.isTraceEnabled() ) {
 1030   			log.trace( "Getting current persistent state for: " + MessageHelper.infoString( this, id, getFactory() ) );
 1031   		}
 1032   
 1033   		try {
 1034   			PreparedStatement ps = session.getBatcher().prepareSelectStatement( getSQLSnapshotSelectString() );
 1035   			try {
 1036   				getIdentifierType().nullSafeSet( ps, id, 1, session );
 1037   				//if ( isVersioned() ) getVersionType().nullSafeSet( ps, version, getIdentifierColumnSpan()+1, session );
 1038   				ResultSet rs = ps.executeQuery();
 1039   				try {
 1040   					//if there is no resulting row, return null
 1041   					if ( !rs.next() ) {
 1042   						return null;
 1043   					}
 1044   
 1045   					//otherwise return the "hydrated" state (ie. associations are not resolved)
 1046   					Type[] types = getPropertyTypes();
 1047   					Object[] values = new Object[types.length];
 1048   					boolean[] includeProperty = getPropertyUpdateability();
 1049   					for ( int i = 0; i < types.length; i++ ) {
 1050   						if ( includeProperty[i] ) {
 1051   							values[i] = types[i].hydrate( rs, getPropertyAliases( "", i ), session, null ); //null owner ok??
 1052   						}
 1053   					}
 1054   					return values;
 1055   				}
 1056   				finally {
 1057   					rs.close();
 1058   				}
 1059   			}
 1060   			finally {
 1061   				session.getBatcher().closeStatement( ps );
 1062   			}
 1063   		}
 1064   		catch ( SQLException sqle ) {
 1065   			throw JDBCExceptionHelper.convert(
 1066   					getFactory().getSQLExceptionConverter(),
 1067   					sqle,
 1068   					"could not retrieve snapshot: " +
 1069   					MessageHelper.infoString( this, id, getFactory() ),
 1070   			        getSQLSnapshotSelectString()
 1071   				);
 1072   		}
 1073   
 1074   	}
 1075   
 1076   	/**
 1077   	 * Generate the SQL that selects the version number by id
 1078   	 */
 1079   	protected String generateSelectVersionString() {
 1080   		SimpleSelect select = new SimpleSelect( getFactory().getDialect() )
 1081   				.setTableName( getVersionedTableName() );
 1082   		if ( isVersioned() ) {
 1083   			select.addColumn( versionColumnName );
 1084   		}
 1085   		else {
 1086   			select.addColumns( rootTableKeyColumnNames );
 1087   		}
 1088   		if ( getFactory().getSettings().isCommentsEnabled() ) {
 1089   			select.setComment( "get version " + getEntityName() );
 1090   		}
 1091   		return select.addCondition( rootTableKeyColumnNames, "=?" ).toStatementString();
 1092   	}
 1093   
 1094   	protected String generateInsertGeneratedValuesSelectString() {
 1095   		return generateGeneratedValuesSelectString( getPropertyInsertGenerationInclusions() );
 1096   	}
 1097   
 1098   	protected String generateUpdateGeneratedValuesSelectString() {
 1099   		return generateGeneratedValuesSelectString( getPropertyUpdateGenerationInclusions() );
 1100   	}
 1101   
 1102   	private String generateGeneratedValuesSelectString(ValueInclusion[] inclusions) {
 1103   		Select select = new Select( getFactory().getDialect() );
 1104   
 1105   		if ( getFactory().getSettings().isCommentsEnabled() ) {
 1106   			select.setComment( "get generated state " + getEntityName() );
 1107   		}
 1108   
 1109   		String[] aliasedIdColumns = StringHelper.qualify( getRootAlias(), getIdentifierColumnNames() );
 1110   
 1111   		// Here we render the select column list based on the properties defined as being generated.
 1112   		// For partial component generation, we currently just re-select the whole component
 1113   		// rather than trying to handle the individual generated portions.
 1114   		String selectClause = concretePropertySelectFragment( getRootAlias(), inclusions );
 1115   		selectClause = selectClause.substring( 2 );
 1116   
 1117   		String fromClause = fromTableFragment( getRootAlias() ) +
 1118   				fromJoinFragment( getRootAlias(), true, false );
 1119   
 1120   		String whereClause = new StringBuffer()
 1121   			.append( StringHelper.join( "=? and ", aliasedIdColumns ) )
 1122   			.append( "=?" )
 1123   			.append( whereJoinFragment( getRootAlias(), true, false ) )
 1124   			.toString();
 1125   
 1126   		return select.setSelectClause( selectClause )
 1127   				.setFromClause( fromClause )
 1128   				.setOuterJoins( "", "" )
 1129   				.setWhereClause( whereClause )
 1130   				.toStatementString();
 1131   	}
 1132   
 1133   	protected static interface InclusionChecker {
 1134   		public boolean includeProperty(int propertyNumber);
 1135   	}
 1136   
 1137   	protected String concretePropertySelectFragment(String alias, final ValueInclusion[] inclusions) {
 1138   		return concretePropertySelectFragment(
 1139   				alias,
 1140   				new InclusionChecker() {
 1141   					// TODO : currently we really do not handle ValueInclusion.PARTIAL...
 1142   					// ValueInclusion.PARTIAL would indicate parts of a component need to
 1143   					// be included in the select; currently we then just render the entire
 1144   					// component into the select clause in that case.
 1145   					public boolean includeProperty(int propertyNumber) {
 1146   						return inclusions[propertyNumber] != ValueInclusion.NONE;
 1147   					}
 1148   				}
 1149   		);
 1150   	}
 1151   
 1152   	protected String concretePropertySelectFragment(String alias, final boolean[] includeProperty) {
 1153   		return concretePropertySelectFragment(
 1154   				alias,
 1155   				new InclusionChecker() {
 1156   					public boolean includeProperty(int propertyNumber) {
 1157   						return includeProperty[propertyNumber];
 1158   					}
 1159   				}
 1160   		);
 1161   	}
 1162   
 1163   	protected String concretePropertySelectFragment(String alias, InclusionChecker inclusionChecker) {
 1164   		int propertyCount = getPropertyNames().length;
 1165   		int[] propertyTableNumbers = getPropertyTableNumbersInSelect();
 1166   		SelectFragment frag = new SelectFragment();
 1167   		for ( int i = 0; i < propertyCount; i++ ) {
 1168   			if ( inclusionChecker.includeProperty( i ) ) {
 1169   				frag.addColumns(
 1170   						generateTableAlias( alias, propertyTableNumbers[i] ),
 1171   						propertyColumnNames[i],
 1172   						propertyColumnAliases[i]
 1173   				);
 1174   				frag.addFormulas(
 1175   						generateTableAlias( alias, propertyTableNumbers[i] ),
 1176   						propertyColumnFormulaTemplates[i],
 1177   						propertyColumnAliases[i]
 1178   				);
 1179   			}
 1180   		}
 1181   		return frag.toFragmentString();
 1182   	}
 1183   
 1184   	protected String generateSnapshotSelectString() {
 1185   
 1186   		//TODO: should we use SELECT .. FOR UPDATE?
 1187   
 1188   		Select select = new Select( getFactory().getDialect() );
 1189   
 1190   		if ( getFactory().getSettings().isCommentsEnabled() ) {
 1191   			select.setComment( "get current state " + getEntityName() );
 1192   		}
 1193   
 1194   		String[] aliasedIdColumns = StringHelper.qualify( getRootAlias(), getIdentifierColumnNames() );
 1195   		String selectClause = StringHelper.join( ", ", aliasedIdColumns ) +
 1196   				concretePropertySelectFragment( getRootAlias(), getPropertyUpdateability() );
 1197   
 1198   		String fromClause = fromTableFragment( getRootAlias() ) +
 1199   				fromJoinFragment( getRootAlias(), true, false );
 1200   
 1201   		String whereClause = new StringBuffer()
 1202   			.append( StringHelper.join( "=? and ",
 1203   					aliasedIdColumns ) )
 1204   			.append( "=?" )
 1205   			.append( whereJoinFragment( getRootAlias(), true, false ) )
 1206   			.toString();
 1207   
 1208   		/*if ( isVersioned() ) {
 1209   			where.append(" and ")
 1210   				.append( getVersionColumnName() )
 1211   				.append("=?");
 1212   		}*/
 1213   
 1214   		return select.setSelectClause( selectClause )
 1215   				.setFromClause( fromClause )
 1216   				.setOuterJoins( "", "" )
 1217   				.setWhereClause( whereClause )
 1218   				.toStatementString();
 1219   	}
 1220   
 1221   	public Object forceVersionIncrement(Serializable id, Object currentVersion, SessionImplementor session) {
 1222   		if ( !isVersioned() ) {
 1223   			throw new AssertionFailure( "cannot force version increment on non-versioned entity" );
 1224   		}
 1225   
 1226   		if ( isVersionPropertyGenerated() ) {
 1227   			// the difficulty here is exactly what do we update in order to
 1228   			// force the version to be incremented in the db...
 1229   			throw new HibernateException( "LockMode.FORCE is currently not supported for generated version properties" );
 1230   		}
 1231   
 1232   		Object nextVersion = getVersionType().next( currentVersion, session );
 1233   		if ( log.isTraceEnabled() ) {
 1234   			log.trace(
 1235   					"Forcing version increment [" + MessageHelper.infoString( this, id, getFactory() ) +
 1236   					"; " + getVersionType().toLoggableString( currentVersion, getFactory() ) +
 1237   					" -> " + getVersionType().toLoggableString( nextVersion, getFactory() ) + "]"
 1238   			);
 1239   		}
 1240   
 1241   		// todo : cache this sql...
 1242   		String versionIncrementString = generateVersionIncrementUpdateString();
 1243   		PreparedStatement st = null;
 1244   		try {
 1245   			try {
 1246   				st = session.getBatcher().prepareStatement( versionIncrementString );
 1247   				getVersionType().nullSafeSet( st, nextVersion, 1, session );
 1248   				getIdentifierType().nullSafeSet( st, id, 2, session );
 1249   				getVersionType().nullSafeSet( st, currentVersion, 2 + getIdentifierColumnSpan(), session );
 1250   				int rows = st.executeUpdate();
 1251   				if ( rows != 1 ) {
 1252   					throw new StaleObjectStateException( getEntityName(), id );
 1253   				}
 1254   			}
 1255   			finally {
 1256   				session.getBatcher().closeStatement( st );
 1257   			}
 1258   		}
 1259   		catch ( SQLException sqle ) {
 1260   			throw JDBCExceptionHelper.convert(
 1261   					getFactory().getSQLExceptionConverter(),
 1262   					sqle,
 1263   					"could not retrieve version: " +
 1264   					MessageHelper.infoString( this, id, getFactory() ),
 1265   					getVersionSelectString()
 1266   				);
 1267   		}
 1268   
 1269   		return nextVersion;
 1270   	}
 1271   
 1272   	private String generateVersionIncrementUpdateString() {
 1273   		Update update = new Update( getFactory().getDialect() );
 1274   		update.setTableName( getTableName( 0 ) );
 1275   		if ( getFactory().getSettings().isCommentsEnabled() ) {
 1276   			update.setComment( "forced version increment" );
 1277   		}
 1278   		update.addColumn( getVersionColumnName() );
 1279   		update.setPrimaryKeyColumnNames( getIdentifierColumnNames() );
 1280   		update.setVersionColumnName( getVersionColumnName() );
 1281   		return update.toStatementString();
 1282   	}
 1283   
 1284   	/**
 1285   	 * Retrieve the version number
 1286   	 */
 1287   	public Object getCurrentVersion(Serializable id, SessionImplementor session) throws HibernateException {
 1288   
 1289   		if ( log.isTraceEnabled() ) {
 1290   			log.trace( "Getting version: " + MessageHelper.infoString( this, id, getFactory() ) );
 1291   		}
 1292   
 1293   		try {
 1294   
 1295   			PreparedStatement st = session.getBatcher().prepareSelectStatement( getVersionSelectString() );
 1296   			try {
 1297   				getIdentifierType().nullSafeSet( st, id, 1, session );
 1298   
 1299   				ResultSet rs = st.executeQuery();
 1300   				try {
 1301   					if ( !rs.next() ) {
 1302   						return null;
 1303   					}
 1304   					if ( !isVersioned() ) {
 1305   						return this;
 1306   					}
 1307   					return getVersionType().nullSafeGet( rs, getVersionColumnName(), session, null );
 1308   				}
 1309   				finally {
 1310   					rs.close();
 1311   				}
 1312   			}
 1313   			finally {
 1314   				session.getBatcher().closeStatement( st );
 1315   			}
 1316   
 1317   		}
 1318   		catch ( SQLException sqle ) {
 1319   			throw JDBCExceptionHelper.convert(
 1320   					getFactory().getSQLExceptionConverter(),
 1321   					sqle,
 1322   					"could not retrieve version: " +
 1323   					MessageHelper.infoString( this, id, getFactory() ),
 1324   					getVersionSelectString()
 1325   				);
 1326   		}
 1327   
 1328   	}
 1329   
 1330   	protected void initLockers() {
 1331   		lockers.put( LockMode.READ, generateLocker( LockMode.READ ) );
 1332   		lockers.put( LockMode.UPGRADE, generateLocker( LockMode.UPGRADE ) );
 1333   		lockers.put( LockMode.UPGRADE_NOWAIT, generateLocker( LockMode.UPGRADE_NOWAIT ) );
 1334   		lockers.put( LockMode.FORCE, generateLocker( LockMode.FORCE ) );
 1335   	}
 1336   
 1337   	protected LockingStrategy generateLocker(LockMode lockMode) {
 1338   		return factory.getDialect().getLockingStrategy( this, lockMode );
 1339   	}
 1340   
 1341   	private LockingStrategy getLocker(LockMode lockMode) {
 1342   		return ( LockingStrategy ) lockers.get( lockMode );
 1343   	}
 1344   
 1345   	public void lock(
 1346   			Serializable id,
 1347   	        Object version,
 1348   	        Object object,
 1349   	        LockMode lockMode,
 1350   	        SessionImplementor session) throws HibernateException {
 1351   		getLocker( lockMode ).lock( id, version, object, session );
 1352   	}
 1353   
 1354   	public String getRootTableName() {
 1355   		return getSubclassTableName( 0 );
 1356   	}
 1357   
 1358   	public String getRootTableAlias(String drivingAlias) {
 1359   		return drivingAlias;
 1360   	}
 1361   
 1362   	public String[] getRootTableIdentifierColumnNames() {
 1363   		return getRootTableKeyColumnNames();
 1364   	}
 1365   
 1366   	public String[] toColumns(String alias, String propertyName) throws QueryException {
 1367   		return propertyMapping.toColumns( alias, propertyName );
 1368   	}
 1369   
 1370   	public String[] toColumns(String propertyName) throws QueryException {
 1371   		return propertyMapping.getColumnNames( propertyName );
 1372   	}
 1373   
 1374   	public Type toType(String propertyName) throws QueryException {
 1375   		return propertyMapping.toType( propertyName );
 1376   	}
 1377   
 1378   	public String[] getPropertyColumnNames(String propertyName) {
 1379   		return propertyMapping.getColumnNames( propertyName );
 1380   	}
 1381   
 1382   	/**
 1383   	 * Warning:
 1384   	 * When there are duplicated property names in the subclasses
 1385   	 * of the class, this method may return the wrong table
 1386   	 * number for the duplicated subclass property (note that
 1387   	 * SingleTableEntityPersister defines an overloaded form
 1388   	 * which takes the entity name.
 1389   	 */
 1390   	public int getSubclassPropertyTableNumber(String propertyPath) {
 1391   		String rootPropertyName = StringHelper.root(propertyPath);
 1392   		Type type = propertyMapping.toType(rootPropertyName);
 1393   		if ( type.isAssociationType() ) {
 1394   			AssociationType assocType = ( AssociationType ) type;
 1395   			if ( assocType.useLHSPrimaryKey() ) {
 1396   				// performance op to avoid the array search
 1397   				return 0;
 1398   			}
 1399   			else if ( type.isCollectionType() ) {
 1400   				// properly handle property-ref-based associations
 1401   				rootPropertyName = assocType.getLHSPropertyName();
 1402   			}
 1403   		}
 1404   		//Enable for HHH-440, which we don't like:
 1405   		/*if ( type.isComponentType() && !propertyName.equals(rootPropertyName) ) {
 1406   			String unrooted = StringHelper.unroot(propertyName);
 1407   			int idx = ArrayHelper.indexOf( getSubclassColumnClosure(), unrooted );
 1408   			if ( idx != -1 ) {
 1409   				return getSubclassColumnTableNumberClosure()[idx];
 1410   			}
 1411   		}*/
 1412   		int index = ArrayHelper.indexOf( getSubclassPropertyNameClosure(), rootPropertyName); //TODO: optimize this better!
 1413   		return index==-1 ? 0 : getSubclassPropertyTableNumber(index);
 1414   	}
 1415   
 1416   	public Declarer getSubclassPropertyDeclarer(String propertyPath) {
 1417   		int tableIndex = getSubclassPropertyTableNumber( propertyPath );
 1418   		if ( tableIndex == 0 ) {
 1419   			return Declarer.CLASS;
 1420   		}
 1421   		else if ( isClassOrSuperclassTable( tableIndex ) ) {
 1422   			return Declarer.SUPERCLASS;
 1423   		}
 1424   		else {
 1425   			return Declarer.SUBCLASS;
 1426   		}
 1427   	}
 1428   
 1429   	protected String generateTableAlias(String rootAlias, int tableNumber) {
 1430   		if ( tableNumber == 0 ) {
 1431   			return rootAlias;
 1432   		}
 1433   		StringBuffer buf = new StringBuffer().append( rootAlias );
 1434   		if ( !rootAlias.endsWith( "_" ) ) {
 1435   			buf.append( '_' );
 1436   		}
 1437   		return buf.append( tableNumber ).append( '_' ).toString();
 1438   	}
 1439   
 1440   	public String[] toColumns(String name, final int i) {
 1441   		final String alias = generateTableAlias( name, getSubclassPropertyTableNumber( i ) );
 1442   		String[] cols = getSubclassPropertyColumnNames( i );
 1443   		String[] templates = getSubclassPropertyFormulaTemplateClosure()[i];
 1444   		String[] result = new String[cols.length];
 1445   		for ( int j = 0; j < cols.length; j++ ) {
 1446   			if ( cols[j] == null ) {
 1447   				result[j] = StringHelper.replace( templates[j], Template.TEMPLATE, alias );
 1448   			}
 1449   			else {
 1450   				result[j] = StringHelper.qualify( alias, cols[j] );
 1451   			}
 1452   		}
 1453   		return result;
 1454   	}
 1455   
 1456   	private int getSubclassPropertyIndex(String propertyName) {
 1457   		return ArrayHelper.indexOf(subclassPropertyNameClosure, propertyName);
 1458   	}
 1459   
 1460   	protected String[] getPropertySubclassNames() {
 1461   		return propertySubclassNames;
 1462   	}
 1463   
 1464   	public String[] getPropertyColumnNames(int i) {
 1465   		return propertyColumnNames[i];
 1466   	}
 1467   
 1468   	protected int getPropertyColumnSpan(int i) {
 1469   		return propertyColumnSpans[i];
 1470   	}
 1471   
 1472   	protected boolean hasFormulaProperties() {
 1473   		return hasFormulaProperties;
 1474   	}
 1475   
 1476   	public FetchMode getFetchMode(int i) {
 1477   		return subclassPropertyFetchModeClosure[i];
 1478   	}
 1479   
 1480   	public CascadeStyle getCascadeStyle(int i) {
 1481   		return subclassPropertyCascadeStyleClosure[i];
 1482   	}
 1483   
 1484   	public Type getSubclassPropertyType(int i) {
 1485   		return subclassPropertyTypeClosure[i];
 1486   	}
 1487   
 1488   	public String getSubclassPropertyName(int i) {
 1489   		return subclassPropertyNameClosure[i];
 1490   	}
 1491   
 1492   	public int countSubclassProperties() {
 1493   		return subclassPropertyTypeClosure.length;
 1494   	}
 1495   
 1496   	public String[] getSubclassPropertyColumnNames(int i) {
 1497   		return subclassPropertyColumnNameClosure[i];
 1498   	}
 1499   
 1500   	public boolean isDefinedOnSubclass(int i) {
 1501   		return propertyDefinedOnSubclass[i];
 1502   	}
 1503   
 1504   	protected String[][] getSubclassPropertyFormulaTemplateClosure() {
 1505   		return subclassPropertyFormulaTemplateClosure;
 1506   	}
 1507   
 1508   	protected Type[] getSubclassPropertyTypeClosure() {
 1509   		return subclassPropertyTypeClosure;
 1510   	}
 1511   
 1512   	protected String[][] getSubclassPropertyColumnNameClosure() {
 1513   		return subclassPropertyColumnNameClosure;
 1514   	}
 1515   
 1516   	protected String[] getSubclassPropertyNameClosure() {
 1517   		return subclassPropertyNameClosure;
 1518   	}
 1519   
 1520   	protected String[] getSubclassPropertySubclassNameClosure() {
 1521   		return subclassPropertySubclassNameClosure;
 1522   	}
 1523   
 1524   	protected String[] getSubclassColumnClosure() {
 1525   		return subclassColumnClosure;
 1526   	}
 1527   
 1528   	protected String[] getSubclassColumnAliasClosure() {
 1529   		return subclassColumnAliasClosure;
 1530   	}
 1531   
 1532   	protected String[] getSubclassFormulaClosure() {
 1533   		return subclassFormulaClosure;
 1534   	}
 1535   
 1536   	protected String[] getSubclassFormulaTemplateClosure() {
 1537   		return subclassFormulaTemplateClosure;
 1538   	}
 1539   
 1540   	protected String[] getSubclassFormulaAliasClosure() {
 1541   		return subclassFormulaAliasClosure;
 1542   	}
 1543   
 1544   	public String[] getSubclassPropertyColumnAliases(String propertyName, String suffix) {
 1545   		String rawAliases[] = ( String[] ) subclassPropertyAliases.get( propertyName );
 1546   
 1547   		if ( rawAliases == null ) {
 1548   			return null;
 1549   		}
 1550   
 1551   		String result[] = new String[rawAliases.length];
 1552   		for ( int i = 0; i < rawAliases.length; i++ ) {
 1553   			result[i] = new Alias( suffix ).toUnquotedAliasString( rawAliases[i] );
 1554   		}
 1555   		return result;
 1556   	}
 1557   
 1558   	public String[] getSubclassPropertyColumnNames(String propertyName) {
 1559   		//TODO: should we allow suffixes on these ?
 1560   		return ( String[] ) subclassPropertyColumnNames.get( propertyName );
 1561   	}
 1562   
 1563   
 1564   
 1565   	//This is really ugly, but necessary:
 1566   	/**
 1567   	 * Must be called by subclasses, at the end of their constructors
 1568   	 */
 1569   	protected void initSubclassPropertyAliasesMap(PersistentClass model) throws MappingException {
 1570   
 1571   		// ALIASES
 1572   		internalInitSubclassPropertyAliasesMap( null, model.getSubclassPropertyClosureIterator() );
 1573   
 1574   		// aliases for identifier ( alias.id ); skip if the entity defines a non-id property named 'id'
 1575   		if ( ! entityMetamodel.hasNonIdentifierPropertyNamedId() ) {
 1576   			subclassPropertyAliases.put( ENTITY_ID, getIdentifierAliases() );
 1577   			subclassPropertyColumnNames.put( ENTITY_ID, getIdentifierColumnNames() );
 1578   		}
 1579   
 1580   		// aliases named identifier ( alias.idname )
 1581   		if ( hasIdentifierProperty() ) {
 1582   			subclassPropertyAliases.put( getIdentifierPropertyName(), getIdentifierAliases() );
 1583   			subclassPropertyColumnNames.put( getIdentifierPropertyName(), getIdentifierColumnNames() );
 1584   		}
 1585   
 1586   		// aliases for composite-id's
 1587   		if ( getIdentifierType().isComponentType() ) {
 1588   			// Fetch embedded identifiers propertynames from the "virtual" identifier component
 1589   			AbstractComponentType componentId = ( AbstractComponentType ) getIdentifierType();
 1590   			String[] idPropertyNames = componentId.getPropertyNames();
 1591   			String[] idAliases = getIdentifierAliases();
 1592   			String[] idColumnNames = getIdentifierColumnNames();
 1593   
 1594   			for ( int i = 0; i < idPropertyNames.length; i++ ) {
 1595   				if ( entityMetamodel.hasNonIdentifierPropertyNamedId() ) {
 1596   					subclassPropertyAliases.put(
 1597   							ENTITY_ID + "." + idPropertyNames[i],
 1598   							new String[] { idAliases[i] }
 1599   					);
 1600   					subclassPropertyColumnNames.put(
 1601   							ENTITY_ID + "." + getIdentifierPropertyName() + "." + idPropertyNames[i],
 1602   							new String[] { idColumnNames[i] }
 1603   					);
 1604   				}
 1605   //				if (hasIdentifierProperty() && !ENTITY_ID.equals( getIdentifierPropertyName() ) ) {
 1606   				if ( hasIdentifierProperty() ) {
 1607   					subclassPropertyAliases.put(
 1608   							getIdentifierPropertyName() + "." + idPropertyNames[i],
 1609   							new String[] { idAliases[i] }
 1610   					);
 1611   					subclassPropertyColumnNames.put(
 1612   							getIdentifierPropertyName() + "." + idPropertyNames[i],
 1613   							new String[] { idColumnNames[i] }
 1614   					);
 1615   				}
 1616   				else {
 1617   					// embedded composite ids ( alias.idname1, alias.idname2 )
 1618   					subclassPropertyAliases.put( idPropertyNames[i], new String[] { idAliases[i] } );
 1619   					subclassPropertyColumnNames.put( idPropertyNames[i],  new String[] { idColumnNames[i] } );
 1620   				}
 1621   			}
 1622   		}
 1623   
 1624   		if ( entityMetamodel.isPolymorphic() ) {
 1625   			subclassPropertyAliases.put( ENTITY_CLASS, new String[] { getDiscriminatorAlias() } );
 1626   			subclassPropertyColumnNames.put( ENTITY_CLASS, new String[] { getDiscriminatorColumnName() } );
 1627   		}
 1628   
 1629   	}
 1630   
 1631   	private void internalInitSubclassPropertyAliasesMap(String path, Iterator propertyIterator) {
 1632   		while ( propertyIterator.hasNext() ) {
 1633   
 1634   			Property prop = ( Property ) propertyIterator.next();
 1635   			String propname = path == null ? prop.getName() : path + "." + prop.getName();
 1636   			if ( prop.isComposite() ) {
 1637   				Component component = ( Component ) prop.getValue();
 1638   				Iterator compProps = component.getPropertyIterator();
 1639   				internalInitSubclassPropertyAliasesMap( propname, compProps );
 1640   			}
 1641   			else {
 1642   				String[] aliases = new String[prop.getColumnSpan()];
 1643   				String[] cols = new String[prop.getColumnSpan()];
 1644   				Iterator colIter = prop.getColumnIterator();
 1645   				int l = 0;
 1646   				while ( colIter.hasNext() ) {
 1647   					Selectable thing = ( Selectable ) colIter.next();
 1648   					aliases[l] = thing.getAlias( getFactory().getDialect(), prop.getValue().getTable() );
 1649   					cols[l] = thing.getText( getFactory().getDialect() ); // TODO: skip formulas?
 1650   					l++;
 1651   				}
 1652   
 1653   				subclassPropertyAliases.put( propname, aliases );
 1654   				subclassPropertyColumnNames.put( propname, cols );
 1655   			}
 1656   		}
 1657   
 1658   	}
 1659   
 1660   	public Object loadByUniqueKey(String propertyName, Object uniqueKey, SessionImplementor session)
 1661   			throws HibernateException {
 1662   		return getAppropriateUniqueKeyLoader( propertyName, session.getEnabledFilters() )
 1663   				.loadByUniqueKey( session, uniqueKey );
 1664   	}
 1665   
 1666   	private EntityLoader getAppropriateUniqueKeyLoader(String propertyName, Map enabledFilters) {
 1667   
 1668   		final boolean useStaticLoader = ( enabledFilters == null || enabledFilters.isEmpty() )
 1669   				&& propertyName.indexOf('.')<0; //ugly little workaround for fact that createUniqueKeyLoaders() does not handle component properties
 1670   
 1671   		if ( useStaticLoader ) {
 1672   			return (EntityLoader) uniqueKeyLoaders.get( propertyName );
 1673   		}
 1674   		else {
 1675   			return createUniqueKeyLoader(
 1676   					propertyMapping.toType(propertyName),
 1677   					propertyMapping.toColumns(propertyName),
 1678   					enabledFilters
 1679   				);
 1680   		}
 1681   	}
 1682   
 1683   	public int getPropertyIndex(String propertyName) {
 1684   		return entityMetamodel.getPropertyIndex(propertyName);
 1685   	}
 1686   
 1687   	protected void createUniqueKeyLoaders() throws MappingException {
 1688   		Type[] propertyTypes = getPropertyTypes();
 1689   		String[] propertyNames = getPropertyNames();
 1690   		for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
 1691   			if ( propertyUniqueness[i] ) {
 1692   				//don't need filters for the static loaders
 1693   				uniqueKeyLoaders.put(
 1694   						propertyNames[i],
 1695   						createUniqueKeyLoader(
 1696   								propertyTypes[i],
 1697   								getPropertyColumnNames( i ),
 1698   								CollectionHelper.EMPTY_MAP
 1699   							)
 1700   					);
 1701   				//TODO: create uk loaders for component properties
 1702   			}
 1703   		}
 1704   	}
 1705   
 1706   	private EntityLoader createUniqueKeyLoader(Type uniqueKeyType, String[] columns, Map enabledFilters) {
 1707   		if ( uniqueKeyType.isEntityType() ) {
 1708   			String className = ( ( EntityType ) uniqueKeyType ).getAssociatedEntityName();
 1709   			uniqueKeyType = getFactory().getEntityPersister( className ).getIdentifierType();
 1710   		}
 1711   
 1712   		return new EntityLoader( this, columns, uniqueKeyType, 1, LockMode.NONE, getFactory(), enabledFilters );
 1713   	}
 1714   
 1715   	protected String getSQLWhereString(String alias) {
 1716   		return StringHelper.replace( sqlWhereStringTemplate, Template.TEMPLATE, alias );
 1717   	}
 1718   
 1719   	protected boolean hasWhere() {
 1720   		return sqlWhereString != null;
 1721   	}
 1722   
 1723   	private void initOrdinaryPropertyPaths(Mapping mapping) throws MappingException {
 1724   		for ( int i = 0; i < getSubclassPropertyNameClosure().length; i++ ) {
 1725   			propertyMapping.initPropertyPaths( getSubclassPropertyNameClosure()[i],
 1726   					getSubclassPropertyTypeClosure()[i],
 1727   					getSubclassPropertyColumnNameClosure()[i],
 1728   					getSubclassPropertyFormulaTemplateClosure()[i],
 1729   					mapping );
 1730   		}
 1731   	}
 1732   
 1733   	private void initIdentifierPropertyPaths(Mapping mapping) throws MappingException {
 1734   		String idProp = getIdentifierPropertyName();
 1735   		if ( idProp != null ) {
 1736   			propertyMapping.initPropertyPaths( idProp, getIdentifierType(), getIdentifierColumnNames(), null, mapping );
 1737   		}
 1738   		if ( entityMetamodel.getIdentifierProperty().isEmbedded() ) {
 1739   			propertyMapping.initPropertyPaths( null, getIdentifierType(), getIdentifierColumnNames(), null, mapping );
 1740   		}
 1741   		if ( ! entityMetamodel.hasNonIdentifierPropertyNamedId() ) {
 1742   			propertyMapping.initPropertyPaths( ENTITY_ID, getIdentifierType(), getIdentifierColumnNames(), null, mapping );
 1743   		}
 1744   	}
 1745   
 1746   	private void initDiscriminatorPropertyPath(Mapping mapping) throws MappingException {
 1747   		propertyMapping.initPropertyPaths( ENTITY_CLASS,
 1748   				getDiscriminatorType(),
 1749   				new String[]{getDiscriminatorColumnName()},
 1750   				new String[]{getDiscriminatorFormulaTemplate()},
 1751   				getFactory() );
 1752   	}
 1753   
 1754   	protected void initPropertyPaths(Mapping mapping) throws MappingException {
 1755   		initOrdinaryPropertyPaths(mapping);
 1756   		initOrdinaryPropertyPaths(mapping); //do two passes, for collection property-ref!
 1757   		initIdentifierPropertyPaths(mapping);
 1758   		if ( entityMetamodel.isPolymorphic() ) {
 1759   			initDiscriminatorPropertyPath( mapping );
 1760   		}
 1761   	}
 1762   
 1763   	protected UniqueEntityLoader createEntityLoader(LockMode lockMode, Map enabledFilters) throws MappingException {
 1764   		//TODO: disable batch loading if lockMode > READ?
 1765   		return BatchingEntityLoader.createBatchingEntityLoader( this, batchSize, lockMode, getFactory(), enabledFilters );
 1766   	}
 1767   
 1768   	protected UniqueEntityLoader createEntityLoader(LockMode lockMode) throws MappingException {
 1769   		return createEntityLoader( lockMode, CollectionHelper.EMPTY_MAP );
 1770   	}
 1771   
 1772   	protected boolean check(int rows, Serializable id, int tableNumber, Expectation expectation, PreparedStatement statement) throws HibernateException {
 1773   		try {
 1774   			expectation.verifyOutcome( rows, statement, -1 );
 1775   		}
 1776   		catch( StaleStateException e ) {
 1777   			if ( !isNullableTable( tableNumber ) ) {
 1778   				if ( getFactory().getStatistics().isStatisticsEnabled() ) {
 1779   					getFactory().getStatisticsImplementor()
 1780   							.optimisticFailure( getEntityName() );
 1781   				}
 1782   				throw new StaleObjectStateException( getEntityName(), id );
 1783   			}
 1784   			return false;
 1785   		}
 1786   		catch( TooManyRowsAffectedException e ) {
 1787   			throw new HibernateException(
 1788   					"Duplicate identifier in table for: " +
 1789   					MessageHelper.infoString( this, id, getFactory() )
 1790   			);
 1791   		}
 1792   		catch ( Throwable t ) {
 1793   			return false;
 1794   		}
 1795   		return true;
 1796   	}
 1797   
 1798   	protected String generateUpdateString(boolean[] includeProperty, int j, boolean useRowId) {
 1799   		return generateUpdateString( includeProperty, j, null, useRowId );
 1800   	}
 1801   
 1802   	/**
 1803   	 * Generate the SQL that updates a row by id (and version)
 1804   	 */
 1805   	protected String generateUpdateString(final boolean[] includeProperty,
 1806   										  final int j,
 1807   										  final Object[] oldFields,
 1808   										  final boolean useRowId) {
 1809   
 1810   		Update update = new Update( getFactory().getDialect() ).setTableName( getTableName( j ) );
 1811   
 1812   		// select the correct row by either pk or rowid
 1813   		if ( useRowId ) {
 1814   			update.setPrimaryKeyColumnNames( new String[]{rowIdName} ); //TODO: eventually, rowIdName[j]
 1815   		}
 1816   		else {
 1817   			update.setPrimaryKeyColumnNames( getKeyColumns( j ) );
 1818   		}
 1819   
 1820   		boolean hasColumns = false;
 1821   		for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
 1822   			if ( includeProperty[i] && isPropertyOfTable( i, j ) ) {
 1823   				// this is a property of the table, which we are updating
 1824   				update.addColumns( getPropertyColumnNames(i), propertyColumnUpdateable[i] );
 1825   				hasColumns = hasColumns || getPropertyColumnSpan( i ) > 0;
 1826   			}
 1827   		}
 1828   
 1829   		if ( j == 0 && isVersioned() && entityMetamodel.getOptimisticLockMode() == Versioning.OPTIMISTIC_LOCK_VERSION ) {
 1830   			// this is the root (versioned) table, and we are using version-based
 1831   			// optimistic locking;  if we are not updating the version, also don't
 1832   			// check it (unless this is a "generated" version column)!
 1833   			if ( checkVersion( includeProperty ) ) {
 1834   				update.setVersionColumnName( getVersionColumnName() );
 1835   				hasColumns = true;
 1836   			}
 1837   		}
 1838   		else if ( entityMetamodel.getOptimisticLockMode() > Versioning.OPTIMISTIC_LOCK_VERSION && oldFields != null ) {
 1839   			// we are using "all" or "dirty" property-based optimistic locking
 1840   
 1841   			boolean[] includeInWhere = entityMetamodel.getOptimisticLockMode() == Versioning.OPTIMISTIC_LOCK_ALL ?
 1842   					getPropertyUpdateability() : //optimistic-lock="all", include all updatable properties
 1843   					includeProperty; //optimistic-lock="dirty", include all properties we are updating this time
 1844   
 1845   			boolean[] versionability = getPropertyVersionability();
 1846   			Type[] types = getPropertyTypes();
 1847   			for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
 1848   				boolean include = includeInWhere[i] &&
 1849   						isPropertyOfTable( i, j ) &&
 1850   						versionability[i];
 1851   				if ( include ) {
 1852   					// this property belongs to the table, and it is not specifically
 1853   					// excluded from optimistic locking by optimistic-lock="false"
 1854   					String[] propertyColumnNames = getPropertyColumnNames( i );
 1855   					boolean[] propertyNullness = types[i].toColumnNullness( oldFields[i], getFactory() );
 1856   					for ( int k=0; k<propertyNullness.length; k++ ) {
 1857   						if ( propertyNullness[k] ) {
 1858   							update.addWhereColumn( propertyColumnNames[k] );
 1859   						}
 1860   						else {
 1861   							update.addWhereColumn( propertyColumnNames[k], " is null" );
 1862   						}
 1863   					}
 1864   				}
 1865   			}
 1866   
 1867   		}
 1868   
 1869   		if ( getFactory().getSettings().isCommentsEnabled() ) {
 1870   			update.setComment( "update " + getEntityName() );
 1871   		}
 1872   
 1873   		return hasColumns ? update.toStatementString() : null;
 1874   	}
 1875   
 1876   	private boolean checkVersion(final boolean[] includeProperty) {
 1877           return includeProperty[ getVersionProperty() ] ||
 1878   				entityMetamodel.getPropertyUpdateGenerationInclusions()[ getVersionProperty() ] != ValueInclusion.NONE;
 1879   	}
 1880   
 1881   	protected String generateInsertString(boolean[] includeProperty, int j) {
 1882   		return generateInsertString( false, includeProperty, j );
 1883   	}
 1884   
 1885   	protected String generateInsertString(boolean identityInsert, boolean[] includeProperty) {
 1886   		return generateInsertString( identityInsert, includeProperty, 0 );
 1887   	}
 1888   
 1889   	/**
 1890   	 * Generate the SQL that inserts a row
 1891   	 */
 1892   	protected String generateInsertString(boolean identityInsert, boolean[] includeProperty, int j) {
 1893   
 1894   		// todo : remove the identityInsert param and variations;
 1895   		//   identity-insert strings are now generated from generateIdentityInsertString()
 1896   
 1897   		Insert insert = new Insert( getFactory().getDialect() )
 1898   				.setTableName( getTableName( j ) );
 1899   
 1900   		// add normal properties
 1901   		for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
 1902   			if ( includeProperty[i] && isPropertyOfTable( i, j ) ) {
 1903   				// this property belongs on the table and is to be inserted
 1904   				insert.addColumns( getPropertyColumnNames(i), propertyColumnInsertable[i] );
 1905   			}
 1906   		}
 1907   
 1908   		// add the discriminator
 1909   		if ( j == 0 ) {
 1910   			addDiscriminatorToInsert( insert );
 1911   		}
 1912   
 1913   		// add the primary key
 1914   		if ( j == 0 && identityInsert ) {
 1915   			insert.addIdentityColumn( getKeyColumns( 0 )[0] );
 1916   		}
 1917   		else {
 1918   			insert.addColumns( getKeyColumns( j ) );
 1919   		}
 1920   
 1921   		if ( getFactory().getSettings().isCommentsEnabled() ) {
 1922   			insert.setComment( "insert " + getEntityName() );
 1923   		}
 1924   
 1925   		String result = insert.toStatementString();
 1926   
 1927   		// append the SQL to return the generated identifier
 1928   		if ( j == 0 && identityInsert && useInsertSelectIdentity() ) { //TODO: suck into Insert
 1929   			result = getFactory().getDialect().appendIdentitySelectToInsert( result );
 1930   		}
 1931   
 1932   		return result;
 1933   	}
 1934   
 1935   	/**
 1936   	 * Used to generate an insery statement against the root table in the
 1937   	 * case of identifier generation strategies where the insert statement
 1938   	 * executions actually generates the identifier value.
 1939   	 *
 1940   	 * @param includeProperty indices of the properties to include in the
 1941   	 * insert statement.
 1942   	 * @return The insert SQL statement string
 1943   	 */
 1944   	protected String generateIdentityInsertString(boolean[] includeProperty) {
 1945   		Insert insert = identityDelegate.prepareIdentifierGeneratingInsert();
 1946   		insert.setTableName( getTableName( 0 ) );
 1947   
 1948   		// add normal properties
 1949   		for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
 1950   			if ( includeProperty[i] && isPropertyOfTable( i, 0 ) ) {
 1951   				// this property belongs on the table and is to be inserted
 1952   				insert.addColumns( getPropertyColumnNames(i), propertyColumnInsertable[i] );
 1953   			}
 1954   		}
 1955   
 1956   		// add the discriminator
 1957   		addDiscriminatorToInsert( insert );
 1958   
 1959   		// delegate already handles PK columns
 1960   
 1961   		if ( getFactory().getSettings().isCommentsEnabled() ) {
 1962   			insert.setComment( "insert " + getEntityName() );
 1963   		}
 1964   
 1965   		return insert.toStatementString();
 1966   	}
 1967   
 1968   	/**
 1969   	 * Generate the SQL that deletes a row by id (and version)
 1970   	 */
 1971   	protected String generateDeleteString(int j) {
 1972   		Delete delete = new Delete()
 1973   				.setTableName( getTableName( j ) )
 1974   				.setPrimaryKeyColumnNames( getKeyColumns( j ) );
 1975   		if ( j == 0 ) {
 1976   			delete.setVersionColumnName( getVersionColumnName() );
 1977   		}
 1978   		if ( getFactory().getSettings().isCommentsEnabled() ) {
 1979   			delete.setComment( "delete " + getEntityName() );
 1980   		}
 1981   		return delete.toStatementString();
 1982   	}
 1983   
 1984   	protected int dehydrate(
 1985   			Serializable id,
 1986   			Object[] fields,
 1987   			boolean[] includeProperty,
 1988   			boolean[][] includeColumns,
 1989   			int j,
 1990   			PreparedStatement st,
 1991   			SessionImplementor session) throws HibernateException, SQLException {
 1992   		return dehydrate( id, fields, null, includeProperty, includeColumns, j, st, session, 1 );
 1993   	}
 1994   
 1995   	/**
 1996   	 * Marshall the fields of a persistent instance to a prepared statement
 1997   	 */
 1998   	protected int dehydrate(
 1999   			final Serializable id,
 2000   	        final Object[] fields,
 2001   	        final Object rowId,
 2002   	        final boolean[] includeProperty,
 2003   	        final boolean[][] includeColumns,
 2004   	        final int j,
 2005   	        final PreparedStatement ps,
 2006   	        final SessionImplementor session,
 2007   	        int index) throws SQLException, HibernateException {
 2008   
 2009   		if ( log.isTraceEnabled() ) {
 2010   			log.trace( "Dehydrating entity: " + MessageHelper.infoString( this, id, getFactory() ) );
 2011   		}
 2012   
 2013   		for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
 2014   			if ( includeProperty[i] && isPropertyOfTable( i, j ) ) {
 2015   				getPropertyTypes()[i].nullSafeSet( ps, fields[i], index, includeColumns[i], session );
 2016   				//index += getPropertyColumnSpan( i );
 2017   				index += ArrayHelper.countTrue( includeColumns[i] ); //TODO:  this is kinda slow...
 2018   			}
 2019   		}
 2020   
 2021   		if ( rowId != null ) {
 2022   			ps.setObject( index, rowId );
 2023   			index += 1;
 2024   		}
 2025   		else if ( id != null ) {
 2026   			getIdentifierType().nullSafeSet( ps, id, index, session );
 2027   			index += getIdentifierColumnSpan();
 2028   		}
 2029   
 2030   		return index;
 2031   
 2032   	}
 2033   
 2034   	/**
 2035   	 * Unmarshall the fields of a persistent instance from a result set,
 2036   	 * without resolving associations or collections. Question: should
 2037   	 * this really be here, or should it be sent back to Loader?
 2038   	 */
 2039   	public Object[] hydrate(
 2040   			final ResultSet rs,
 2041   	        final Serializable id,
 2042   	        final Object object,
 2043   	        final Loadable rootLoadable,
 2044   	        final String[][] suffixedPropertyColumns,
 2045   	        final boolean allProperties,
 2046   	        final SessionImplementor session) throws SQLException, HibernateException {
 2047   
 2048   		if ( log.isTraceEnabled() ) {
 2049   			log.trace( "Hydrating entity: " + MessageHelper.infoString( this, id, getFactory() ) );
 2050   		}
 2051   
 2052   		final AbstractEntityPersister rootPersister = (AbstractEntityPersister) rootLoadable;
 2053   
 2054   		final boolean hasDeferred = rootPersister.hasSequentialSelect();
 2055   		PreparedStatement sequentialSelect = null;
 2056   		ResultSet sequentialResultSet = null;
 2057   		boolean sequentialSelectEmpty = false;
 2058   		try {
 2059   
 2060   			if ( hasDeferred ) {
 2061   				final String sql = rootPersister.getSequentialSelect( getEntityName() );
 2062   				if ( sql != null ) {
 2063   					//TODO: I am not so sure about the exception handling in this bit!
 2064   					sequentialSelect = session.getBatcher().prepareSelectStatement( sql );
 2065   					rootPersister.getIdentifierType().nullSafeSet( sequentialSelect, id, 1, session );
 2066   					sequentialResultSet = sequentialSelect.executeQuery();
 2067   					if ( !sequentialResultSet.next() ) {
 2068   						// TODO: Deal with the "optional" attribute in the <join> mapping;
 2069   						// this code assumes that optional defaults to "true" because it
 2070   						// doesn't actually seem to work in the fetch="join" code
 2071   						//
 2072   						// Note that actual proper handling of optional-ality here is actually
 2073   						// more involved than this patch assumes.  Remember that we might have
 2074   						// multiple <join/> mappings associated with a single entity.  Really
 2075   						// a couple of things need to happen to properly handle optional here:
 2076   						//  1) First and foremost, when handling multiple <join/>s, we really
 2077   						//      should be using the entity root table as the driving table;
 2078   						//      another option here would be to choose some non-optional joined
 2079   						//      table to use as the driving table.  In all likelihood, just using
 2080   						//      the root table is much simplier
 2081   						//  2) Need to add the FK columns corresponding to each joined table
 2082   						//      to the generated select list; these would then be used when
 2083   						//      iterating the result set to determine whether all non-optional
 2084   						//      data is present
 2085   						// My initial thoughts on the best way to deal with this would be
 2086   						// to introduce a new SequentialSelect abstraction that actually gets
 2087   						// generated in the persisters (ok, SingleTable...) and utilized here.
 2088   						// It would encapsulated all this required optional-ality checking...
 2089   						sequentialSelectEmpty = true;
 2090   					}
 2091   				}
 2092   			}
 2093   
 2094   			final String[] propNames = getPropertyNames();
 2095   			final Type[] types = getPropertyTypes();
 2096   			final Object[] values = new Object[types.length];
 2097   			final boolean[] laziness = getPropertyLaziness();
 2098   			final String[] propSubclassNames = getSubclassPropertySubclassNameClosure();
 2099   
 2100   			for ( int i = 0; i < types.length; i++ ) {
 2101   				if ( !propertySelectable[i] ) {
 2102   					values[i] = BackrefPropertyAccessor.UNKNOWN;
 2103   				}
 2104   				else if ( allProperties || !laziness[i] ) {
 2105   					//decide which ResultSet to get the property value from:
 2106   					final boolean propertyIsDeferred = hasDeferred &&
 2107   							rootPersister.isSubclassPropertyDeferred( propNames[i], propSubclassNames[i] );
 2108   					if ( propertyIsDeferred && sequentialSelectEmpty ) {
 2109   						values[i] = null;
 2110   					}
 2111   					else {
 2112   						final ResultSet propertyResultSet = propertyIsDeferred ? sequentialResultSet : rs;
 2113   						final String[] cols = propertyIsDeferred ? propertyColumnAliases[i] : suffixedPropertyColumns[i];
 2114   						values[i] = types[i].hydrate( propertyResultSet, cols, session, object );
 2115   					}
 2116   				}
 2117   				else {
 2118   					values[i] = LazyPropertyInitializer.UNFETCHED_PROPERTY;
 2119   				}
 2120   			}
 2121   
 2122   			if ( sequentialResultSet != null ) {
 2123   				sequentialResultSet.close();
 2124   			}
 2125   
 2126   			return values;
 2127   
 2128   		}
 2129   		finally {
 2130   			if ( sequentialSelect != null ) {
 2131   				session.getBatcher().closeStatement( sequentialSelect );
 2132   			}
 2133   		}
 2134   	}
 2135   
 2136   	protected boolean useInsertSelectIdentity() {
 2137   		return !useGetGeneratedKeys() && getFactory().getDialect().supportsInsertSelectIdentity();
 2138   	}
 2139   
 2140   	protected boolean useGetGeneratedKeys() {
 2141   		return getFactory().getSettings().isGetGeneratedKeysEnabled();
 2142   	}
 2143   
 2144   	protected String getSequentialSelect(String entityName) {
 2145   		throw new UnsupportedOperationException("no sequential selects");
 2146   	}
 2147   
 2148   	/**
 2149   	 * Perform an SQL INSERT, and then retrieve a generated identifier.
 2150   	 * <p/>
 2151   	 * This form is used for PostInsertIdentifierGenerator-style ids (IDENTITY,
 2152   	 * select, etc).
 2153   	 */
 2154   	protected Serializable insert(
 2155   			final Object[] fields,
 2156   	        final boolean[] notNull,
 2157   	        String sql,
 2158   	        final Object object,
 2159   	        final SessionImplementor session) throws HibernateException {
 2160   
 2161   		if ( log.isTraceEnabled() ) {
 2162   			log.trace( "Inserting entity: " + getEntityName() + " (native id)" );
 2163   			if ( isVersioned() ) {
 2164   				log.trace( "Version: " + Versioning.getVersion( fields, this ) );
 2165   			}
 2166   		}
 2167   
 2168   		Binder binder = new Binder() {
 2169   			public void bindValues(PreparedStatement ps) throws SQLException {
 2170   				dehydrate( null, fields, notNull, propertyColumnInsertable, 0, ps, session );
 2171   			}
 2172   			public Object getEntity() {
 2173   				return object;
 2174   			}
 2175   		};
 2176   		return identityDelegate.performInsert( sql, session, binder );
 2177   	}
 2178   
 2179   	public String getIdentitySelectString() {
 2180   		//TODO: cache this in an instvar
 2181   		return getFactory().getDialect().getIdentitySelectString(
 2182   				getTableName(0),
 2183   				getKeyColumns(0)[0],
 2184   				getIdentifierType().sqlTypes( getFactory() )[0]
 2185   		);
 2186   	}
 2187   
 2188   	public String getSelectByUniqueKeyString(String propertyName) {
 2189   		return new SimpleSelect( getFactory().getDialect() )
 2190   			.setTableName( getTableName(0) )
 2191   			.addColumns( getKeyColumns(0) )
 2192   			.addCondition( getPropertyColumnNames(propertyName), "=?" )
 2193   			.toStatementString();
 2194   	}
 2195   
 2196   	/**
 2197   	 * Perform an SQL INSERT.
 2198   	 * <p/>
 2199   	 * This for is used for all non-root tables as well as the root table
 2200   	 * in cases where the identifier value is known before the insert occurs.
 2201   	 */
 2202   	protected void insert(
 2203   			final Serializable id,
 2204   	        final Object[] fields,
 2205   	        final boolean[] notNull,
 2206   	        final int j,
 2207   	        final String sql,
 2208   	        final Object object,
 2209   	        final SessionImplementor session) throws HibernateException {
 2210   
 2211   		if ( isInverseTable( j ) ) {
 2212   			return;
 2213   		}
 2214   
 2215   		//note: it is conceptually possible that a UserType could map null to
 2216   		//	  a non-null value, so the following is arguable:
 2217   		if ( isNullableTable( j ) && isAllNull( fields, j ) ) {
 2218   			return;
 2219   		}
 2220   
 2221   		if ( log.isTraceEnabled() ) {
 2222   			log.trace( "Inserting entity: " + MessageHelper.infoString( this, id, getFactory() ) );
 2223   			if ( j == 0 && isVersioned() ) {
 2224   				log.trace( "Version: " + Versioning.getVersion( fields, this ) );
 2225   			}
 2226   		}
 2227   
 2228   		Expectation expectation = Expectations.appropriateExpectation( insertResultCheckStyles[j] );
 2229   		boolean callable = isInsertCallable( j );
 2230   		// we can't batch joined inserts, *especially* not if it is an identity insert;
 2231   		// nor can we batch statements where the expectation is based on an output param
 2232   		final boolean useBatch = j == 0 && expectation.canBeBatched();
 2233   		try {
 2234   
 2235   			// Render the SQL query
 2236   			final PreparedStatement insert;
 2237   			if ( useBatch ) {
 2238   				if ( callable ) {
 2239   					insert = session.getBatcher().prepareBatchCallableStatement( sql );
 2240   				}
 2241   				else {
 2242   					insert = session.getBatcher().prepareBatchStatement( sql );
 2243   				}
 2244   			}
 2245   			else {
 2246   				if ( callable ) {
 2247   					insert = session.getBatcher().prepareCallableStatement( sql );
 2248   				}
 2249   				else {
 2250   					insert = session.getBatcher().prepareStatement( sql );
 2251   				}
 2252   			}
 2253   
 2254   			try {
 2255   				int index = 1;
 2256   				index += expectation.prepare( insert );
 2257   
 2258   				// Write the values of fields onto the prepared statement - we MUST use the state at the time the
 2259   				// insert was issued (cos of foreign key constraints). Not necessarily the object's current state
 2260   
 2261   				dehydrate( id, fields, null, notNull, propertyColumnInsertable, j, insert, session, index );
 2262   
 2263   				if ( useBatch ) {
 2264   					// TODO : shouldnt inserts be Expectations.NONE?
 2265   					session.getBatcher().addToBatch( expectation );
 2266   				}
 2267   				else {
 2268   					expectation.verifyOutcome( insert.executeUpdate(), insert, -1 );
 2269   				}
 2270   
 2271   			}
 2272   			catch ( SQLException sqle ) {
 2273   				if ( useBatch ) {
 2274   					session.getBatcher().abortBatch( sqle );
 2275   				}
 2276   				throw sqle;
 2277   			}
 2278   			finally {
 2279   				if ( !useBatch ) {
 2280   					session.getBatcher().closeStatement( insert );
 2281   				}
 2282   			}
 2283   		}
 2284   		catch ( SQLException sqle ) {
 2285   			throw JDBCExceptionHelper.convert(
 2286   					getFactory().getSQLExceptionConverter(),
 2287   					sqle,
 2288   					"could not insert: " + MessageHelper.infoString( this ),
 2289   					sql
 2290   				);
 2291   		}
 2292   
 2293   	}
 2294   
 2295   	/**
 2296   	 * Perform an SQL UPDATE or SQL INSERT
 2297   	 */
 2298   	protected void updateOrInsert(
 2299   			final Serializable id,
 2300   	        final Object[] fields,
 2301   	        final Object[] oldFields,
 2302   	        final Object rowId,
 2303   	        final boolean[] includeProperty,
 2304   	        final int j,
 2305   	        final Object oldVersion,
 2306   	        final Object object,
 2307   	        final String sql,
 2308   	        final SessionImplementor session) throws HibernateException {
 2309   
 2310   		if ( !isInverseTable( j ) ) {
 2311   
 2312   			final boolean isRowToUpdate;
 2313   			if ( isNullableTable( j ) && oldFields != null && isAllNull( oldFields, j ) ) {
 2314   				//don't bother trying to update, we know there is no row there yet
 2315   				isRowToUpdate = false;
 2316   			}
 2317   			else if ( isNullableTable( j ) && isAllNull( fields, j ) ) {
 2318   				//if all fields are null, we might need to delete existing row
 2319   				isRowToUpdate = true;
 2320   				delete( id, oldVersion, j, object, getSQLDeleteStrings()[j], session, null );
 2321   			}
 2322   			else {
 2323   				//there is probably a row there, so try to update
 2324   				//if no rows were updated, we will find out
 2325   				isRowToUpdate = update( id, fields, oldFields, rowId, includeProperty, j, oldVersion, object, sql, session );
 2326   			}
 2327   
 2328   			if ( !isRowToUpdate && !isAllNull( fields, j ) ) {
 2329   				// assume that the row was not there since it previously had only null
 2330   				// values, so do an INSERT instead
 2331   				//TODO: does not respect dynamic-insert
 2332   				insert( id, fields, getPropertyInsertability(), j, getSQLInsertStrings()[j], object, session );
 2333   			}
 2334   
 2335   		}
 2336   
 2337   	}
 2338   
 2339   	protected boolean update(
 2340   			final Serializable id,
 2341   	        final Object[] fields,
 2342   	        final Object[] oldFields,
 2343   	        final Object rowId,
 2344   	        final boolean[] includeProperty,
 2345   	        final int j,
 2346   	        final Object oldVersion,
 2347   	        final Object object,
 2348   	        final String sql,
 2349   	        final SessionImplementor session) throws HibernateException {
 2350   
 2351   		final boolean useVersion = j == 0 && isVersioned();
 2352   		final Expectation expectation = Expectations.appropriateExpectation( updateResultCheckStyles[j] );
 2353   		final boolean callable = isUpdateCallable( j );
 2354   		final boolean useBatch = j == 0 && expectation.canBeBatched() && isBatchable(); //note: updates to joined tables can't be batched...
 2355   
 2356   		if ( log.isTraceEnabled() ) {
 2357   			log.trace( "Updating entity: " + MessageHelper.infoString( this, id, getFactory() ) );
 2358   			if ( useVersion ) {
 2359   				log.trace( "Existing version: " + oldVersion + " -> New version: " + fields[getVersionProperty()] );
 2360   			}
 2361   		}
 2362   
 2363   		try {
 2364   
 2365   			int index = 1; // starting index
 2366   			final PreparedStatement update;
 2367   			if ( useBatch ) {
 2368   				if ( callable ) {
 2369   					update = session.getBatcher().prepareBatchCallableStatement( sql );
 2370   				}
 2371   				else {
 2372   					update = session.getBatcher().prepareBatchStatement( sql );
 2373   				}
 2374   			}
 2375   			else {
 2376   				if ( callable ) {
 2377   					update = session.getBatcher().prepareCallableStatement( sql );
 2378   				}
 2379   				else {
 2380   					update = session.getBatcher().prepareStatement( sql );
 2381   				}
 2382   			}
 2383   
 2384   			try {
 2385   
 2386   				index+= expectation.prepare( update );
 2387   
 2388   				//Now write the values of fields onto the prepared statement
 2389   				index = dehydrate( id, fields, rowId, includeProperty, propertyColumnUpdateable, j, update, session, index );
 2390   
 2391   				// Write any appropriate versioning conditional parameters
 2392   				if ( useVersion && Versioning.OPTIMISTIC_LOCK_VERSION == entityMetamodel.getOptimisticLockMode() ) {
 2393   					if ( checkVersion( includeProperty ) ) {
 2394   						getVersionType().nullSafeSet( update, oldVersion, index, session );
 2395   					}
 2396   				}
 2397   				else if ( entityMetamodel.getOptimisticLockMode() > Versioning.OPTIMISTIC_LOCK_VERSION && oldFields != null ) {
 2398   					boolean[] versionability = getPropertyVersionability(); //TODO: is this really necessary????
 2399   					boolean[] includeOldField = entityMetamodel.getOptimisticLockMode() == Versioning.OPTIMISTIC_LOCK_ALL ?
 2400   							getPropertyUpdateability() : includeProperty;
 2401   					Type[] types = getPropertyTypes();
 2402   					for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
 2403   						boolean include = includeOldField[i] &&
 2404   								isPropertyOfTable( i, j ) &&
 2405   								versionability[i]; //TODO: is this really necessary????
 2406   						if ( include ) {
 2407   							boolean[] settable = types[i].toColumnNullness( oldFields[i], getFactory() );
 2408   							types[i].nullSafeSet(
 2409   									update,
 2410   									oldFields[i],
 2411   									index,
 2412   									settable,
 2413   									session
 2414   								);
 2415   							index += ArrayHelper.countTrue(settable);
 2416   						}
 2417   					}
 2418   				}
 2419   
 2420   				if ( useBatch ) {
 2421   					session.getBatcher().addToBatch( expectation );
 2422   					return true;
 2423   				}
 2424   				else {
 2425   					return check( update.executeUpdate(), id, j, expectation, update );
 2426   				}
 2427   
 2428   			}
 2429   			catch ( SQLException sqle ) {
 2430   				if ( useBatch ) {
 2431   					session.getBatcher().abortBatch( sqle );
 2432   				}
 2433   				throw sqle;
 2434   			}
 2435   			finally {
 2436   				if ( !useBatch ) {
 2437   					session.getBatcher().closeStatement( update );
 2438   				}
 2439   			}
 2440   
 2441   		}
 2442   		catch ( SQLException sqle ) {
 2443   			throw JDBCExceptionHelper.convert(
 2444   					getFactory().getSQLExceptionConverter(),
 2445   					sqle,
 2446   					"could not update: " + MessageHelper.infoString( this, id, getFactory() ),
 2447   					sql
 2448   				);
 2449   		}
 2450   	}
 2451   
 2452   	/**
 2453   	 * Perform an SQL DELETE
 2454   	 */
 2455   	protected void delete(
 2456   			final Serializable id,
 2457   			final Object version,
 2458   			final int j,
 2459   			final Object object,
 2460   			final String sql,
 2461   			final SessionImplementor session,
 2462   			final Object[] loadedState) throws HibernateException {
 2463   
 2464   		if ( isInverseTable( j ) ) {
 2465   			return;
 2466   		}
 2467   
 2468   		final boolean useVersion = j == 0 && isVersioned();
 2469   		final boolean callable = isDeleteCallable( j );
 2470   		final Expectation expectation = Expectations.appropriateExpectation( deleteResultCheckStyles[j] );
 2471   		final boolean useBatch = j == 0 && isBatchable() && expectation.canBeBatched();
 2472   
 2473   		if ( log.isTraceEnabled() ) {
 2474   			log.trace( "Deleting entity: " + MessageHelper.infoString( this, id, getFactory() ) );
 2475   			if ( useVersion ) {
 2476   				log.trace( "Version: " + version );
 2477   			}
 2478   		}
 2479   
 2480   		if ( isTableCascadeDeleteEnabled( j ) ) {
 2481   			if ( log.isTraceEnabled() ) {
 2482   				log.trace( "delete handled by foreign key constraint: " + getTableName( j ) );
 2483   			}
 2484   			return; //EARLY EXIT!
 2485   		}
 2486   
 2487   		try {
 2488   
 2489   			//Render the SQL query
 2490   			PreparedStatement delete;
 2491   			int index = 1;
 2492   			if ( useBatch ) {
 2493   				if ( callable ) {
 2494   					delete = session.getBatcher().prepareBatchCallableStatement( sql );
 2495   				}
 2496   				else {
 2497   					delete = session.getBatcher().prepareBatchStatement( sql );
 2498   				}
 2499   			}
 2500   			else {
 2501   				if ( callable ) {
 2502   					delete = session.getBatcher().prepareCallableStatement( sql );
 2503   				}
 2504   				else {
 2505   					delete = session.getBatcher().prepareStatement( sql );
 2506   				}
 2507   			}
 2508   
 2509   			try {
 2510   
 2511   				index += expectation.prepare( delete );
 2512   
 2513   				// Do the key. The key is immutable so we can use the _current_ object state - not necessarily
 2514   				// the state at the time the delete was issued
 2515   				getIdentifierType().nullSafeSet( delete, id, index, session );
 2516   				index += getIdentifierColumnSpan();
 2517   
 2518   				// We should use the _current_ object state (ie. after any updates that occurred during flush)
 2519   
 2520   				if ( useVersion ) {
 2521   					getVersionType().nullSafeSet( delete, version, index, session );
 2522   				}
 2523   				else if ( entityMetamodel.getOptimisticLockMode() > Versioning.OPTIMISTIC_LOCK_VERSION && loadedState != null ) {
 2524   					boolean[] versionability = getPropertyVersionability();
 2525   					Type[] types = getPropertyTypes();
 2526   					for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
 2527   						if ( isPropertyOfTable( i, j ) && versionability[i] ) {
 2528   							// this property belongs to the table and it is not specifically
 2529   							// excluded from optimistic locking by optimistic-lock="false"
 2530   							boolean[] settable = types[i].toColumnNullness( loadedState[i], getFactory() );
 2531   							types[i].nullSafeSet( delete, loadedState[i], index, settable, session );
 2532   							index += ArrayHelper.countTrue( settable );
 2533   						}
 2534   					}
 2535   				}
 2536   
 2537   				if ( useBatch ) {
 2538   					session.getBatcher().addToBatch( expectation );
 2539   				}
 2540   				else {
 2541   					check( delete.executeUpdate(), id, j, expectation, delete );
 2542   				}
 2543   
 2544   			}
 2545   			catch ( SQLException sqle ) {
 2546   				if ( useBatch ) {
 2547   					session.getBatcher().abortBatch( sqle );
 2548   				}
 2549   				throw sqle;
 2550   			}
 2551   			finally {
 2552   				if ( !useBatch ) {
 2553   					session.getBatcher().closeStatement( delete );
 2554   				}
 2555   			}
 2556   
 2557   		}
 2558   		catch ( SQLException sqle ) {
 2559   			throw JDBCExceptionHelper.convert(
 2560   					getFactory().getSQLExceptionConverter(),
 2561   					sqle,
 2562   					"could not delete: " +
 2563   					MessageHelper.infoString( this, id, getFactory() ),
 2564   					sql
 2565   				);
 2566   
 2567   		}
 2568   
 2569   	}
 2570   
 2571   	private String[] getUpdateStrings(boolean byRowId, boolean lazy) {
 2572   		if ( byRowId ) {
 2573   			return lazy ? getSQLLazyUpdateByRowIdStrings() : getSQLUpdateByRowIdStrings();
 2574   		}
 2575   		else {
 2576   			return lazy ? getSQLLazyUpdateStrings() : getSQLUpdateStrings();
 2577   		}
 2578   	}
 2579   
 2580   	/**
 2581   	 * Update an object
 2582   	 */
 2583   	public void update(
 2584   			final Serializable id,
 2585   	        final Object[] fields,
 2586   	        final int[] dirtyFields,
 2587   	        final boolean hasDirtyCollection,
 2588   	        final Object[] oldFields,
 2589   	        final Object oldVersion,
 2590   	        final Object object,
 2591   	        final Object rowId,
 2592   	        final SessionImplementor session) throws HibernateException {
 2593   
 2594   		//note: dirtyFields==null means we had no snapshot, and we couldn't get one using select-before-update
 2595   		//	  oldFields==null just means we had no snapshot to begin with (we might have used select-before-update to get the dirtyFields)
 2596   
 2597   		final boolean[] tableUpdateNeeded = getTableUpdateNeeded( dirtyFields, hasDirtyCollection );
 2598   		final int span = getTableSpan();
 2599   
 2600   		final boolean[] propsToUpdate;
 2601   		final String[] updateStrings;
 2602   		if ( entityMetamodel.isDynamicUpdate() && dirtyFields != null ) {
 2603   			// For the case of dynamic-update="true", we need to generate the UPDATE SQL
 2604   			propsToUpdate = getPropertiesToUpdate( dirtyFields, hasDirtyCollection );
 2605   			// don't need to check laziness (dirty checking algorithm handles that)
 2606   			updateStrings = new String[span];
 2607   			for ( int j = 0; j < span; j++ ) {
 2608   				updateStrings[j] = tableUpdateNeeded[j] ?
 2609   						generateUpdateString( propsToUpdate, j, oldFields, j == 0 && rowId != null ) :
 2610   						null;
 2611   			}
 2612   		}
 2613   		else {
 2614   			// For the case of dynamic-update="false", or no snapshot, we use the static SQL
 2615   			updateStrings = getUpdateStrings(
 2616   					rowId != null,
 2617   					hasUninitializedLazyProperties( object, session.getEntityMode() )
 2618   				);
 2619   			propsToUpdate = getPropertyUpdateability( object, session.getEntityMode() );
 2620   		}
 2621   
 2622   		for ( int j = 0; j < span; j++ ) {
 2623   			// Now update only the tables with dirty properties (and the table with the version number)
 2624   			if ( tableUpdateNeeded[j] ) {
 2625   				updateOrInsert(
 2626   						id,
 2627   						fields,
 2628   						oldFields,
 2629   						j == 0 ? rowId : null,
 2630   						propsToUpdate,
 2631   						j,
 2632   						oldVersion,
 2633   						object,
 2634   						updateStrings[j],
 2635   						session
 2636   					);
 2637   			}
 2638   		}
 2639   	}
 2640   
 2641   	public Serializable insert(Object[] fields, Object object, SessionImplementor session)
 2642   			throws HibernateException {
 2643   
 2644   		final int span = getTableSpan();
 2645   		final Serializable id;
 2646   		if ( entityMetamodel.isDynamicInsert() ) {
 2647   			// For the case of dynamic-insert="true", we need to generate the INSERT SQL
 2648   			boolean[] notNull = getPropertiesToInsert( fields );
 2649   			id = insert( fields, notNull, generateInsertString( true, notNull ), object, session );
 2650   			for ( int j = 1; j < span; j++ ) {
 2651   				insert( id, fields, notNull, j, generateInsertString( notNull, j ), object, session );
 2652   			}
 2653   		}
 2654   		else {
 2655   			// For the case of dynamic-insert="false", use the static SQL
 2656   			id = insert( fields, getPropertyInsertability(), getSQLIdentityInsertString(), object, session );
 2657   			for ( int j = 1; j < span; j++ ) {
 2658   				insert( id, fields, getPropertyInsertability(), j, getSQLInsertStrings()[j], object, session );
 2659   			}
 2660   		}
 2661   		return id;
 2662   	}
 2663   
 2664   	public void insert(Serializable id, Object[] fields, Object object, SessionImplementor session)
 2665   			throws HibernateException {
 2666   
 2667   		final int span = getTableSpan();
 2668   		if ( entityMetamodel.isDynamicInsert() ) {
 2669   			// For the case of dynamic-insert="true", we need to generate the INSERT SQL
 2670   			boolean[] notNull = getPropertiesToInsert( fields );
 2671   			for ( int j = 0; j < span; j++ ) {
 2672   				insert( id, fields, notNull, j, generateInsertString( notNull, j ), object, session );
 2673   			}
 2674   		}
 2675   		else {
 2676   			// For the case of dynamic-insert="false", use the static SQL
 2677   			for ( int j = 0; j < span; j++ ) {
 2678   				insert( id, fields, getPropertyInsertability(), j, getSQLInsertStrings()[j], object, session );
 2679   			}
 2680   		}
 2681   	}
 2682   
 2683   	/**
 2684   	 * Delete an object
 2685   	 */
 2686   	public void delete(Serializable id, Object version, Object object, SessionImplementor session)
 2687   			throws HibernateException {
 2688   		final int span = getTableSpan();
 2689   		boolean isImpliedOptimisticLocking = !entityMetamodel.isVersioned() && entityMetamodel.getOptimisticLockMode() > Versioning.OPTIMISTIC_LOCK_VERSION;
 2690   		Object[] loadedState = null;
 2691   		if ( isImpliedOptimisticLocking ) {
 2692   			// need to treat this as if it where optimistic-lock="all" (dirty does *not* make sense);
 2693   			// first we need to locate the "loaded" state
 2694   			//
 2695   			// Note, it potentially could be a proxy, so perform the location the safe way...
 2696   			EntityKey key = new EntityKey( id, this, session.getEntityMode() );
 2697   			Object entity = session.getPersistenceContext().getEntity( key );
 2698   			if ( entity != null ) {
 2699   				EntityEntry entry = session.getPersistenceContext().getEntry( entity );
 2700   				loadedState = entry.getLoadedState();
 2701   			}
 2702   		}
 2703   
 2704   		final String[] deleteStrings;
 2705   		if ( isImpliedOptimisticLocking && loadedState != null ) {
 2706   			// we need to utilize dynamic delete statements
 2707   			deleteStrings = generateSQLDeletStrings( loadedState );
 2708   		}
 2709   		else {
 2710   			// otherwise, utilize the static delete statements
 2711   			deleteStrings = getSQLDeleteStrings();
 2712   		}
 2713   
 2714   		for ( int j = span - 1; j >= 0; j-- ) {
 2715   			delete( id, version, j, object, deleteStrings[j], session, loadedState );
 2716   		}
 2717   
 2718   	}
 2719   
 2720   	private String[] generateSQLDeletStrings(Object[] loadedState) {
 2721   		int span = getTableSpan();
 2722   		String[] deleteStrings = new String[span];
 2723   		for ( int j = span - 1; j >= 0; j-- ) {
 2724   			Delete delete = new Delete()
 2725   					.setTableName( getTableName( j ) )
 2726   					.setPrimaryKeyColumnNames( getKeyColumns( j ) );
 2727   			if ( getFactory().getSettings().isCommentsEnabled() ) {
 2728   				delete.setComment( "delete " + getEntityName() + " [" + j + "]" );
 2729   			}
 2730   
 2731   			boolean[] versionability = getPropertyVersionability();
 2732   			Type[] types = getPropertyTypes();
 2733   			for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
 2734   				if ( isPropertyOfTable( i, j ) && versionability[i] ) {
 2735   					// this property belongs to the table and it is not specifically
 2736   					// excluded from optimistic locking by optimistic-lock="false"
 2737   					String[] propertyColumnNames = getPropertyColumnNames( i );
 2738   					boolean[] propertyNullness = types[i].toColumnNullness( loadedState[i], getFactory() );
 2739   					for ( int k = 0; k < propertyNullness.length; k++ ) {
 2740   						if ( propertyNullness[k] ) {
 2741   							delete.addWhereFragment( propertyColumnNames[k] + " = ?" );
 2742   						}
 2743   						else {
 2744   							delete.addWhereFragment( propertyColumnNames[k] + " is null" );
 2745   						}
 2746   					}
 2747   				}
 2748   			}
 2749   			deleteStrings[j] = delete.toStatementString();
 2750   		}
 2751   		return deleteStrings;
 2752   	}
 2753   
 2754   	protected void logStaticSQL() {
 2755   		if ( log.isDebugEnabled() ) {
 2756   			log.debug( "Static SQL for entity: " + getEntityName() );
 2757   			if ( sqlLazySelectString != null ) {
 2758   				log.debug( " Lazy select: " + sqlLazySelectString );
 2759   			}
 2760   			if ( sqlVersionSelectString != null ) {
 2761   				log.debug( " Version select: " + sqlVersionSelectString );
 2762   			}
 2763   			if ( sqlSnapshotSelectString != null ) {
 2764   				log.debug( " Snapshot select: " + sqlSnapshotSelectString );
 2765   			}
 2766   			for ( int j = 0; j < getTableSpan(); j++ ) {
 2767   				log.debug( " Insert " + j + ": " + getSQLInsertStrings()[j] );
 2768   				log.debug( " Update " + j + ": " + getSQLUpdateStrings()[j] );
 2769   				log.debug( " Delete " + j + ": " + getSQLDeleteStrings()[j] );
 2770   
 2771   			}
 2772   			if ( sqlIdentityInsertString != null ) {
 2773   				log.debug( " Identity insert: " + sqlIdentityInsertString );
 2774   			}
 2775   			if ( sqlUpdateByRowIdString != null ) {
 2776   				log.debug( " Update by row id (all fields): " + sqlUpdateByRowIdString );
 2777   			}
 2778   			if ( sqlLazyUpdateByRowIdString != null ) {
 2779   				log.debug( " Update by row id (non-lazy fields): " + sqlLazyUpdateByRowIdString );
 2780   			}
 2781   			if ( sqlInsertGeneratedValuesSelectString != null ) {
 2782   				log.debug( "Insert-generated property select: " + sqlInsertGeneratedValuesSelectString );
 2783   			}
 2784   			if ( sqlUpdateGeneratedValuesSelectString != null ) {
 2785   				log.debug( "Update-generated property select: " + sqlUpdateGeneratedValuesSelectString );
 2786   			}
 2787   		}
 2788   	}
 2789   
 2790   	public String filterFragment(String alias, Map enabledFilters) throws MappingException {
 2791   		final StringBuffer sessionFilterFragment = new StringBuffer();
 2792   		filterHelper.render( sessionFilterFragment, generateFilterConditionAlias( alias ), enabledFilters );
 2793   
 2794   		return sessionFilterFragment.append( filterFragment( alias ) ).toString();
 2795   	}
 2796   
 2797   	public String generateFilterConditionAlias(String rootAlias) {
 2798   		return rootAlias;
 2799   	}
 2800   
 2801   	public String oneToManyFilterFragment(String alias) throws MappingException {
 2802   		return "";
 2803   	}
 2804   
 2805   	public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
 2806   		return getSubclassTableSpan() == 1 ?
 2807   				"" : //just a performance opt!
 2808   				createJoin( alias, innerJoin, includeSubclasses ).toFromFragmentString();
 2809   	}
 2810   
 2811   	public String whereJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
 2812   		return getSubclassTableSpan() == 1 ?
 2813   				"" : //just a performance opt!
 2814   				createJoin( alias, innerJoin, includeSubclasses ).toWhereFragmentString();
 2815   	}
 2816   
 2817   	protected boolean isSubclassTableLazy(int j) {
 2818   		return false;
 2819   	}
 2820   
 2821   	protected JoinFragment createJoin(String name, boolean innerJoin, boolean includeSubclasses) {
 2822   		final String[] idCols = StringHelper.qualify( name, getIdentifierColumnNames() ); //all joins join to the pk of the driving table
 2823   		final JoinFragment join = getFactory().getDialect().createOuterJoinFragment();
 2824   		final int tableSpan = getSubclassTableSpan();
 2825   		for ( int j = 1; j < tableSpan; j++ ) { //notice that we skip the first table; it is the driving table!
 2826   			final boolean joinIsIncluded = isClassOrSuperclassTable( j ) ||
 2827   					( includeSubclasses && !isSubclassTableSequentialSelect( j ) && !isSubclassTableLazy( j ) );
 2828   			if ( joinIsIncluded ) {
 2829   				join.addJoin( getSubclassTableName( j ),
 2830   						generateTableAlias( name, j ),
 2831   						idCols,
 2832   						getSubclassTableKeyColumns( j ),
 2833   						innerJoin && isClassOrSuperclassTable( j ) && !isInverseTable( j ) && !isNullableTable( j ) ?
 2834   						JoinFragment.INNER_JOIN : //we can inner join to superclass tables (the row MUST be there)
 2835   						JoinFragment.LEFT_OUTER_JOIN //we can never inner join to subclass tables
 2836   					);
 2837   			}
 2838   		}
 2839   		return join;
 2840   	}
 2841   
 2842   	protected JoinFragment createJoin(int[] tableNumbers, String drivingAlias) {
 2843   		final String[] keyCols = StringHelper.qualify( drivingAlias, getSubclassTableKeyColumns( tableNumbers[0] ) );
 2844   		final JoinFragment jf = getFactory().getDialect().createOuterJoinFragment();
 2845   		for ( int i = 1; i < tableNumbers.length; i++ ) { //skip the driving table
 2846   			final int j = tableNumbers[i];
 2847   			jf.addJoin( getSubclassTableName( j ),
 2848   					generateTableAlias( getRootAlias(), j ),
 2849   					keyCols,
 2850   					getSubclassTableKeyColumns( j ),
 2851   					isInverseSubclassTable( j ) || isNullableSubclassTable( j ) ?
 2852   					JoinFragment.LEFT_OUTER_JOIN :
 2853   					JoinFragment.INNER_JOIN );
 2854   		}
 2855   		return jf;
 2856   	}
 2857   
 2858   	protected SelectFragment createSelect(final int[] subclassColumnNumbers,
 2859   										  final int[] subclassFormulaNumbers) {
 2860   
 2861   		SelectFragment selectFragment = new SelectFragment();
 2862   
 2863   		int[] columnTableNumbers = getSubclassColumnTableNumberClosure();
 2864   		String[] columnAliases = getSubclassColumnAliasClosure();
 2865   		String[] columns = getSubclassColumnClosure();
 2866   		for ( int i = 0; i < subclassColumnNumbers.length; i++ ) {
 2867   			int columnNumber = subclassColumnNumbers[i];
 2868   			if ( subclassColumnSelectableClosure[columnNumber] ) {
 2869   				final String subalias = generateTableAlias( getRootAlias(), columnTableNumbers[columnNumber] );
 2870   				selectFragment.addColumn( subalias, columns[columnNumber], columnAliases[columnNumber] );
 2871   			}
 2872   		}
 2873   
 2874   		int[] formulaTableNumbers = getSubclassFormulaTableNumberClosure();
 2875   		String[] formulaTemplates = getSubclassFormulaTemplateClosure();
 2876   		String[] formulaAliases = getSubclassFormulaAliasClosure();
 2877   		for ( int i = 0; i < subclassFormulaNumbers.length; i++ ) {
 2878   			int formulaNumber = subclassFormulaNumbers[i];
 2879   			final String subalias = generateTableAlias( getRootAlias(), formulaTableNumbers[formulaNumber] );
 2880   			selectFragment.addFormula( subalias, formulaTemplates[formulaNumber], formulaAliases[formulaNumber] );
 2881   		}
 2882   
 2883   		return selectFragment;
 2884   	}
 2885   
 2886   	protected String createFrom(int tableNumber, String alias) {
 2887   		return getSubclassTableName( tableNumber ) + ' ' + alias;
 2888   	}
 2889   
 2890   	protected String createWhereByKey(int tableNumber, String alias) {
 2891   		//TODO: move to .sql package, and refactor with similar things!
 2892   		return StringHelper.join( "=? and ",
 2893   				StringHelper.qualify( alias, getSubclassTableKeyColumns( tableNumber ) ) ) + "=?";
 2894   	}
 2895   
 2896   	protected String renderSelect(
 2897   			final int[] tableNumbers,
 2898   	        final int[] columnNumbers,
 2899   	        final int[] formulaNumbers) {
 2900   
 2901   		Arrays.sort( tableNumbers ); //get 'em in the right order (not that it really matters)
 2902   
 2903   		//render the where and from parts
 2904   		int drivingTable = tableNumbers[0];
 2905   		final String drivingAlias = generateTableAlias( getRootAlias(), drivingTable ); //we *could* regerate this inside each called method!
 2906   		final String where = createWhereByKey( drivingTable, drivingAlias );
 2907   		final String from = createFrom( drivingTable, drivingAlias );
 2908   
 2909   		//now render the joins
 2910   		JoinFragment jf = createJoin( tableNumbers, drivingAlias );
 2911   
 2912   		//now render the select clause
 2913   		SelectFragment selectFragment = createSelect( columnNumbers, formulaNumbers );
 2914   
 2915   		//now tie it all together
 2916   		Select select = new Select( getFactory().getDialect() );
 2917   		select.setSelectClause( selectFragment.toFragmentString().substring( 2 ) );
 2918   		select.setFromClause( from );
 2919   		select.setWhereClause( where );
 2920   		select.setOuterJoins( jf.toFromFragmentString(), jf.toWhereFragmentString() );
 2921   		if ( getFactory().getSettings().isCommentsEnabled() ) {
 2922   			select.setComment( "sequential select " + getEntityName() );
 2923   		}
 2924   		return select.toStatementString();
 2925   	}
 2926   
 2927   	private String getRootAlias() {
 2928   		return StringHelper.generateAlias( getEntityName() );
 2929   	}
 2930   
 2931   	protected void postConstruct(Mapping mapping) throws MappingException {
 2932   		initPropertyPaths(mapping);
 2933   
 2934   		//insert/update/delete SQL
 2935   		final int joinSpan = getTableSpan();
 2936   		sqlDeleteStrings = new String[joinSpan];
 2937   		sqlInsertStrings = new String[joinSpan];
 2938   		sqlUpdateStrings = new String[joinSpan];
 2939   		sqlLazyUpdateStrings = new String[joinSpan];
 2940   
 2941   		sqlUpdateByRowIdString = rowIdName == null ?
 2942   				null :
 2943   				generateUpdateString( getPropertyUpdateability(), 0, true );
 2944   		sqlLazyUpdateByRowIdString = rowIdName == null ?
 2945   				null :
 2946   				generateUpdateString( getNonLazyPropertyUpdateability(), 0, true );
 2947   
 2948   		for ( int j = 0; j < joinSpan; j++ ) {
 2949   			sqlInsertStrings[j] = customSQLInsert[j] == null ?
 2950   					generateInsertString( getPropertyInsertability(), j ) :
 2951   					customSQLInsert[j];
 2952   			sqlUpdateStrings[j] = customSQLUpdate[j] == null ?
 2953   					generateUpdateString( getPropertyUpdateability(), j, false ) :
 2954   					customSQLUpdate[j];
 2955   			sqlLazyUpdateStrings[j] = customSQLUpdate[j] == null ?
 2956   					generateUpdateString( getNonLazyPropertyUpdateability(), j, false ) :
 2957   					customSQLUpdate[j];
 2958   			sqlDeleteStrings[j] = customSQLDelete[j] == null ?
 2959   					generateDeleteString( j ) :
 2960   					customSQLDelete[j];
 2961   		}
 2962   
 2963   		tableHasColumns = new boolean[joinSpan];
 2964   		for ( int j = 0; j < joinSpan; j++ ) {
 2965   			tableHasColumns[j] = sqlUpdateStrings[j] != null;
 2966   		}
 2967   
 2968   		//select SQL
 2969   		sqlSnapshotSelectString = generateSnapshotSelectString();
 2970   		sqlLazySelectString = generateLazySelectString();
 2971   		sqlVersionSelectString = generateSelectVersionString();
 2972   		if ( hasInsertGeneratedProperties() ) {
 2973   			sqlInsertGeneratedValuesSelectString = generateInsertGeneratedValuesSelectString();
 2974   		}
 2975   		if ( hasUpdateGeneratedProperties() ) {
 2976   			sqlUpdateGeneratedValuesSelectString = generateUpdateGeneratedValuesSelectString();
 2977   		}
 2978   		if ( isIdentifierAssignedByInsert() ) {
 2979   			identityDelegate = ( ( PostInsertIdentifierGenerator ) getIdentifierGenerator() )
 2980   					.getInsertGeneratedIdentifierDelegate( this, getFactory().getDialect(), useGetGeneratedKeys() );
 2981   			sqlIdentityInsertString = customSQLInsert[0] == null
 2982   					? generateIdentityInsertString( getPropertyInsertability() )
 2983   					: customSQLInsert[0];
 2984   		}
 2985   		else {
 2986   			sqlIdentityInsertString = null;
 2987   		}
 2988   
 2989   		logStaticSQL();
 2990   
 2991   	}
 2992   
 2993   	public void postInstantiate() throws MappingException {
 2994   
 2995   		createLoaders();
 2996   		createUniqueKeyLoaders();
 2997   		createQueryLoader();
 2998   
 2999   	}
 3000   
 3001   	private void createLoaders() {
 3002   		loaders.put( LockMode.NONE, createEntityLoader( LockMode.NONE ) );
 3003   
 3004   		UniqueEntityLoader readLoader = createEntityLoader( LockMode.READ );
 3005   		loaders.put( LockMode.READ, readLoader );
 3006   
 3007   		//TODO: inexact, what we really need to know is: are any outer joins used?
 3008   		boolean disableForUpdate = getSubclassTableSpan() > 1 &&
 3009   				hasSubclasses() &&
 3010   				!getFactory().getDialect().supportsOuterJoinForUpdate();
 3011   
 3012   		loaders.put(
 3013   				LockMode.UPGRADE,
 3014   				disableForUpdate ?
 3015   						readLoader :
 3016   						createEntityLoader( LockMode.UPGRADE )
 3017   			);
 3018   		loaders.put(
 3019   				LockMode.UPGRADE_NOWAIT,
 3020   				disableForUpdate ?
 3021   						readLoader :
 3022   						createEntityLoader( LockMode.UPGRADE_NOWAIT )
 3023   			);
 3024   		loaders.put(
 3025   				LockMode.FORCE,
 3026   				disableForUpdate ?
 3027   						readLoader :
 3028   						createEntityLoader( LockMode.FORCE )
 3029   			);
 3030   
 3031   		loaders.put(
 3032   				"merge",
 3033   				new CascadeEntityLoader( this, CascadingAction.MERGE, getFactory() )
 3034   			);
 3035   		loaders.put(
 3036   				"refresh",
 3037   				new CascadeEntityLoader( this, CascadingAction.REFRESH, getFactory() )
 3038   			);
 3039   	}
 3040   
 3041   	protected void createQueryLoader() {
 3042   		if ( loaderName != null ) {
 3043   			queryLoader = new NamedQueryLoader( loaderName, this );
 3044   		}
 3045   	}
 3046   
 3047   	/**
 3048   	 * Load an instance using either the <tt>forUpdateLoader</tt> or the outer joining <tt>loader</tt>,
 3049   	 * depending upon the value of the <tt>lock</tt> parameter
 3050   	 */
 3051   	public Object load(Serializable id, Object optionalObject, LockMode lockMode, SessionImplementor session)
 3052   			throws HibernateException {
 3053   
 3054   		if ( log.isTraceEnabled() ) {
 3055   			log.trace(
 3056   					"Fetching entity: " +
 3057   					MessageHelper.infoString( this, id, getFactory() )
 3058   				);
 3059   		}
 3060   
 3061   		final UniqueEntityLoader loader = getAppropriateLoader( lockMode, session );
 3062   		return loader.load( id, optionalObject, session );
 3063   	}
 3064   
 3065   	private UniqueEntityLoader getAppropriateLoader(LockMode lockMode, SessionImplementor session) {
 3066   		final Map enabledFilters = session.getEnabledFilters();
 3067   		if ( queryLoader != null ) {
 3068   			return queryLoader;
 3069   		}
 3070   		else if ( enabledFilters == null || enabledFilters.isEmpty() ) {
 3071   			if ( session.getFetchProfile()!=null && LockMode.UPGRADE.greaterThan(lockMode) ) {
 3072   				return (UniqueEntityLoader) loaders.get( session.getFetchProfile() );
 3073   			}
 3074   			else {
 3075   				return (UniqueEntityLoader) loaders.get( lockMode );
 3076   			}
 3077   		}
 3078   		else {
 3079   			return createEntityLoader( lockMode, enabledFilters );
 3080   		}
 3081   	}
 3082   
 3083   	private boolean isAllNull(Object[] array, int tableNumber) {
 3084   		for ( int i = 0; i < array.length; i++ ) {
 3085   			if ( isPropertyOfTable( i, tableNumber ) && array[i] != null ) {
 3086   				return false;
 3087   			}
 3088   		}
 3089   		return true;
 3090   	}
 3091   
 3092   	public boolean isSubclassPropertyNullable(int i) {
 3093   		return subclassPropertyNullabilityClosure[i];
 3094   	}
 3095   
 3096   	/**
 3097   	 * Transform the array of property indexes to an array of booleans,
 3098   	 * true when the property is dirty
 3099   	 */
 3100   	protected final boolean[] getPropertiesToUpdate(final int[] dirtyProperties, final boolean hasDirtyCollection) {
 3101   		final boolean[] propsToUpdate = new boolean[ entityMetamodel.getPropertySpan() ];
 3102   		final boolean[] updateability = getPropertyUpdateability(); //no need to check laziness, dirty checking handles that
 3103   		for ( int j = 0; j < dirtyProperties.length; j++ ) {
 3104   			int property = dirtyProperties[j];
 3105   			if ( updateability[property] ) {
 3106   				propsToUpdate[property] = true;
 3107   			}
 3108   		}
 3109   		if ( isVersioned() && updateability[getVersionProperty() ]) {
 3110   			propsToUpdate[ getVersionProperty() ] =
 3111   				Versioning.isVersionIncrementRequired( dirtyProperties, hasDirtyCollection, getPropertyVersionability() );
 3112   		}
 3113   		return propsToUpdate;
 3114   	}
 3115   
 3116   	/**
 3117   	 * Transform the array of property indexes to an array of booleans,
 3118   	 * true when the property is insertable and non-null
 3119   	 */
 3120   	protected boolean[] getPropertiesToInsert(Object[] fields) {
 3121   		boolean[] notNull = new boolean[fields.length];
 3122   		boolean[] insertable = getPropertyInsertability();
 3123   		for ( int i = 0; i < fields.length; i++ ) {
 3124   			notNull[i] = insertable[i] && fields[i] != null;
 3125   		}
 3126   		return notNull;
 3127   	}
 3128   
 3129   	/**
 3130   	 * Locate the property-indices of all properties considered to be dirty.
 3131   	 *
 3132   	 * @param currentState The current state of the entity (the state to be checked).
 3133   	 * @param previousState The previous state of the entity (the state to be checked against).
 3134   	 * @param entity The entity for which we are checking state dirtiness.
 3135   	 * @param session The session in which the check is ccurring.
 3136   	 * @return <tt>null</tt> or the indices of the dirty properties
 3137   	 * @throws HibernateException
 3138   	 */
 3139   	public int[] findDirty(Object[] currentState, Object[] previousState, Object entity, SessionImplementor session)
 3140   	throws HibernateException {
 3141   		int[] props = TypeFactory.findDirty(
 3142   				entityMetamodel.getProperties(),
 3143   				currentState,
 3144   				previousState,
 3145   				propertyColumnUpdateable,
 3146   				hasUninitializedLazyProperties( entity, session.getEntityMode() ),
 3147   				session
 3148   			);
 3149   		if ( props == null ) {
 3150   			return null;
 3151   		}
 3152   		else {
 3153   			logDirtyProperties( props );
 3154   			return props;
 3155   		}
 3156   	}
 3157   
 3158   	/**
 3159   	 * Locate the property-indices of all properties considered to be dirty.
 3160   	 *
 3161   	 * @param old The old state of the entity.
 3162   	 * @param current The current state of the entity.
 3163   	 * @param entity The entity for which we are checking state modification.
 3164   	 * @param session The session in which the check is ccurring.
 3165   	 * @return <tt>null</tt> or the indices of the modified properties
 3166   	 * @throws HibernateException
 3167   	 */
 3168   	public int[] findModified(Object[] old, Object[] current, Object entity, SessionImplementor session)
 3169   	throws HibernateException {
 3170   		int[] props = TypeFactory.findModified(
 3171   				entityMetamodel.getProperties(),
 3172   				current,
 3173   				old,
 3174   				propertyColumnUpdateable,
 3175   				hasUninitializedLazyProperties( entity, session.getEntityMode() ),
 3176   				session
 3177   			);
 3178   		if ( props == null ) {
 3179   			return null;
 3180   		}
 3181   		else {
 3182   			logDirtyProperties( props );
 3183   			return props;
 3184   		}
 3185   	}
 3186   
 3187   	/**
 3188   	 * Which properties appear in the SQL update?
 3189   	 * (Initialized, updateable ones!)
 3190   	 */
 3191   	protected boolean[] getPropertyUpdateability(Object entity, EntityMode entityMode) {
 3192   		return hasUninitializedLazyProperties( entity, entityMode ) ?
 3193   				getNonLazyPropertyUpdateability() :
 3194   				getPropertyUpdateability();
 3195   	}
 3196   
 3197   	private void logDirtyProperties(int[] props) {
 3198   		if ( log.isTraceEnabled() ) {
 3199   			for ( int i = 0; i < props.length; i++ ) {
 3200   				String propertyName = entityMetamodel.getProperties()[ props[i] ].getName();
 3201   				log.trace( StringHelper.qualify( getEntityName(), propertyName ) + " is dirty" );
 3202   			}
 3203   		}
 3204   	}
 3205   
 3206   	protected EntityTuplizer getTuplizer(SessionImplementor session) {
 3207   		return getTuplizer( session.getEntityMode() );
 3208   	}
 3209   
 3210   	protected EntityTuplizer getTuplizer(EntityMode entityMode) {
 3211   		return entityMetamodel.getTuplizer( entityMode );
 3212   	}
 3213   
 3214   	public SessionFactoryImplementor getFactory() {
 3215   		return factory;
 3216   	}
 3217   
 3218   	public EntityMetamodel getEntityMetamodel() {
 3219   		return entityMetamodel;
 3220   	}
 3221   
 3222   	public boolean hasCache() {
 3223   		return cacheAccessStrategy != null;
 3224   	}
 3225   
 3226   	public EntityRegionAccessStrategy getCacheAccessStrategy() {
 3227   		return cacheAccessStrategy;
 3228   	}
 3229   
 3230   	public CacheEntryStructure getCacheEntryStructure() {
 3231   		return cacheEntryStructure;
 3232   	}
 3233   
 3234   	public Comparator getVersionComparator() {
 3235   		return isVersioned() ? getVersionType().getComparator() : null;
 3236   	}
 3237   
 3238   	// temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 3239   	public final String getEntityName() {
 3240   		return entityMetamodel.getName();
 3241   	}
 3242   
 3243   	public EntityType getEntityType() {
 3244   		return entityMetamodel.getEntityType();
 3245   	}
 3246   
 3247   	public boolean isPolymorphic() {
 3248   		return entityMetamodel.isPolymorphic();
 3249   	}
 3250   
 3251   	public boolean isInherited() {
 3252   		return entityMetamodel.isInherited();
 3253   	}
 3254   
 3255   	public boolean hasCascades() {
 3256   		return entityMetamodel.hasCascades();
 3257   	}
 3258   
 3259   	public boolean hasIdentifierProperty() {
 3260   		return !entityMetamodel.getIdentifierProperty().isVirtual();
 3261   	}
 3262   
 3263   	public VersionType getVersionType() {
 3264   		return ( VersionType ) locateVersionType();
 3265   	}
 3266   
 3267   	private Type locateVersionType() {
 3268   		return entityMetamodel.getVersionProperty() == null ?
 3269   				null :
 3270   				entityMetamodel.getVersionProperty().getType();
 3271   	}
 3272   
 3273   	public int getVersionProperty() {
 3274   		return entityMetamodel.getVersionPropertyIndex();
 3275   	}
 3276   
 3277   	public boolean isVersioned() {
 3278   		return entityMetamodel.isVersioned();
 3279   	}
 3280   
 3281   	public boolean isIdentifierAssignedByInsert() {
 3282   		return entityMetamodel.getIdentifierProperty().isIdentifierAssignedByInsert();
 3283   	}
 3284   
 3285   	public boolean hasLazyProperties() {
 3286   		return entityMetamodel.hasLazyProperties();
 3287   	}
 3288   
 3289   //	public boolean hasUninitializedLazyProperties(Object entity) {
 3290   //		if ( hasLazyProperties() ) {
 3291   //			InterceptFieldCallback callback = ( ( InterceptFieldEnabled ) entity ).getInterceptFieldCallback();
 3292   //			return callback != null && !( ( FieldInterceptor ) callback ).isInitialized();
 3293   //		}
 3294   //		else {
 3295   //			return false;
 3296   //		}
 3297   //	}
 3298   
 3299   	public void afterReassociate(Object entity, SessionImplementor session) {
 3300   		//if ( hasLazyProperties() ) {
 3301   		if ( FieldInterceptionHelper.isInstrumented( entity ) ) {
 3302   			FieldInterceptor interceptor = FieldInterceptionHelper.extractFieldInterceptor( entity );
 3303   			if ( interceptor != null ) {
 3304   				interceptor.setSession( session );
 3305   			}
 3306   			else {
 3307   				FieldInterceptor fieldInterceptor = FieldInterceptionHelper.injectFieldInterceptor(
 3308   						entity,
 3309   						getEntityName(),
 3310   						null,
 3311   						session
 3312   				);
 3313   				fieldInterceptor.dirty();
 3314   			}
 3315   		}
 3316   	}
 3317   
 3318   	public Boolean isTransient(Object entity, SessionImplementor session) throws HibernateException {
 3319   		final Serializable id;
 3320   		if ( canExtractIdOutOfEntity() ) {
 3321   			id = getIdentifier( entity, session.getEntityMode() );
 3322   		}
 3323   		else {
 3324   			id = null;
 3325   		}
 3326   		// we *always* assume an instance with a null
 3327   		// identifier or no identifier property is unsaved!
 3328   		if ( id == null ) {
 3329   			return Boolean.TRUE;
 3330   		}
 3331   
 3332   		// check the version unsaved-value, if appropriate
 3333   		final Object version = getVersion( entity, session.getEntityMode() );
 3334   		if ( isVersioned() ) {
 3335   			// let this take precedence if defined, since it works for
 3336   			// assigned identifiers
 3337   			Boolean result = entityMetamodel.getVersionProperty()
 3338   					.getUnsavedValue().isUnsaved( version );
 3339   			if ( result != null ) {
 3340   				return result;
 3341   			}
 3342   		}
 3343   
 3344   		// check the id unsaved-value
 3345   		Boolean result = entityMetamodel.getIdentifierProperty()
 3346   				.getUnsavedValue().isUnsaved( id );
 3347   		if ( result != null ) {
 3348   			return result;
 3349   		}
 3350   
 3351   		// check to see if it is in the second-level cache
 3352   		if ( hasCache() ) {
 3353   			CacheKey ck = new CacheKey(
 3354   					id,
 3355   					getIdentifierType(),
 3356   					getRootEntityName(),
 3357   					session.getEntityMode(),
 3358   					session.getFactory()
 3359   				);
 3360   			if ( getCacheAccessStrategy().get( ck, session.getTimestamp() ) != null ) {
 3361   				return Boolean.FALSE;
 3362   			}
 3363   		}
 3364   
 3365   		return null;
 3366   	}
 3367   
 3368   	public boolean hasCollections() {
 3369   		return entityMetamodel.hasCollections();
 3370   	}
 3371   
 3372   	public boolean hasMutableProperties() {
 3373   		return entityMetamodel.hasMutableProperties();
 3374   	}
 3375   
 3376   	public boolean isMutable() {
 3377   		return entityMetamodel.isMutable();
 3378   	}
 3379   
 3380   	public boolean isAbstract() {
 3381   		return entityMetamodel.isAbstract();
 3382   	}
 3383   
 3384   	public boolean hasSubclasses() {
 3385   		return entityMetamodel.hasSubclasses();
 3386   	}
 3387   
 3388   	public boolean hasProxy() {
 3389   		return entityMetamodel.isLazy();
 3390   	}
 3391   
 3392   	public IdentifierGenerator getIdentifierGenerator() throws HibernateException {
 3393   		return entityMetamodel.getIdentifierProperty().getIdentifierGenerator();
 3394   	}
 3395   
 3396   	public String getRootEntityName() {
 3397   		return entityMetamodel.getRootName();
 3398   	}
 3399   
 3400   	public ClassMetadata getClassMetadata() {
 3401   		return this;
 3402   	}
 3403   
 3404   	public String getMappedSuperclass() {
 3405   		return entityMetamodel.getSuperclass();
 3406   	}
 3407   
 3408   	public boolean isExplicitPolymorphism() {
 3409   		return entityMetamodel.isExplicitPolymorphism();
 3410   	}
 3411   
 3412   	protected boolean useDynamicUpdate() {
 3413   		return entityMetamodel.isDynamicUpdate();
 3414   	}
 3415   
 3416   	protected boolean useDynamicInsert() {
 3417   		return entityMetamodel.isDynamicInsert();
 3418   	}
 3419   
 3420   	protected boolean hasEmbeddedCompositeIdentifier() {
 3421   		return entityMetamodel.getIdentifierProperty().isEmbedded();
 3422   	}
 3423   
 3424   	public boolean canExtractIdOutOfEntity() {
 3425   		return hasIdentifierProperty() || hasEmbeddedCompositeIdentifier() || hasIdentifierMapper();
 3426   	}
 3427   
 3428   	private boolean hasIdentifierMapper() {
 3429   		return entityMetamodel.getIdentifierProperty().hasIdentifierMapper();
 3430   	}
 3431   
 3432   	public String[] getKeyColumnNames() {
 3433   		return getIdentifierColumnNames();
 3434   	}
 3435   
 3436   	public String getName() {
 3437   		return getEntityName();
 3438   	}
 3439   
 3440   	public boolean isCollection() {
 3441   		return false;
 3442   	}
 3443   
 3444   	public boolean consumesEntityAlias() {
 3445   		return true;
 3446   	}
 3447   
 3448   	public boolean consumesCollectionAlias() {
 3449   		return false;
 3450   	}
 3451   
 3452   	public Type getPropertyType(String propertyName) throws MappingException {
 3453   		return propertyMapping.toType(propertyName);
 3454   	}
 3455   
 3456   	public Type getType() {
 3457   		return entityMetamodel.getEntityType();
 3458   	}
 3459   
 3460   	public boolean isSelectBeforeUpdateRequired() {
 3461   		return entityMetamodel.isSelectBeforeUpdate();
 3462   	}
 3463   
 3464   	protected final int optimisticLockMode() {
 3465   		return entityMetamodel.getOptimisticLockMode();
 3466   	}
 3467   
 3468   	public Object createProxy(Serializable id, SessionImplementor session) throws HibernateException {
 3469   		return entityMetamodel.getTuplizer( session.getEntityMode() )
 3470   				.createProxy( id, session );
 3471   	}
 3472   
 3473   	public String toString() {
 3474   		return StringHelper.unqualify( getClass().getName() ) +
 3475   				'(' + entityMetamodel.getName() + ')';
 3476   	}
 3477   
 3478   	public final String selectFragment(
 3479   			Joinable rhs,
 3480   			String rhsAlias,
 3481   			String lhsAlias,
 3482   			String entitySuffix,
 3483   			String collectionSuffix,
 3484   			boolean includeCollectionColumns) {
 3485   		return selectFragment( lhsAlias, entitySuffix );
 3486   	}
 3487   
 3488   	public boolean isInstrumented(EntityMode entityMode) {
 3489   		EntityTuplizer tuplizer = entityMetamodel.getTuplizerOrNull(entityMode);
 3490   		return tuplizer!=null && tuplizer.isInstrumented();
 3491   	}
 3492   
 3493   	public boolean hasInsertGeneratedProperties() {
 3494   		return entityMetamodel.hasInsertGeneratedValues();
 3495   	}
 3496   
 3497   	public boolean hasUpdateGeneratedProperties() {
 3498   		return entityMetamodel.hasUpdateGeneratedValues();
 3499   	}
 3500   
 3501   	public boolean isVersionPropertyGenerated() {
 3502   		return isVersioned() && ( getPropertyUpdateGenerationInclusions() [ getVersionProperty() ] != ValueInclusion.NONE );
 3503   	}
 3504   
 3505   	public boolean isVersionPropertyInsertable() {
 3506   		return isVersioned() && getPropertyInsertability() [ getVersionProperty() ];
 3507   	}
 3508   
 3509   	public void afterInitialize(Object entity, boolean lazyPropertiesAreUnfetched, SessionImplementor session) {
 3510   		getTuplizer( session ).afterInitialize( entity, lazyPropertiesAreUnfetched, session );
 3511   	}
 3512   
 3513   	public String[] getPropertyNames() {
 3514   		return entityMetamodel.getPropertyNames();
 3515   	}
 3516   
 3517   	public Type[] getPropertyTypes() {
 3518   		return entityMetamodel.getPropertyTypes();
 3519   	}
 3520   
 3521   	public boolean[] getPropertyLaziness() {
 3522   		return entityMetamodel.getPropertyLaziness();
 3523   	}
 3524   
 3525   	public boolean[] getPropertyUpdateability() {
 3526   		return entityMetamodel.getPropertyUpdateability();
 3527   	}
 3528   
 3529   	public boolean[] getPropertyCheckability() {
 3530   		return entityMetamodel.getPropertyCheckability();
 3531   	}
 3532   
 3533   	public boolean[] getNonLazyPropertyUpdateability() {
 3534   		return entityMetamodel.getNonlazyPropertyUpdateability();
 3535   	}
 3536   
 3537   	public boolean[] getPropertyInsertability() {
 3538   		return entityMetamodel.getPropertyInsertability();
 3539   	}
 3540   
 3541   	public ValueInclusion[] getPropertyInsertGenerationInclusions() {
 3542   		return entityMetamodel.getPropertyInsertGenerationInclusions();
 3543   	}
 3544   
 3545   	public ValueInclusion[] getPropertyUpdateGenerationInclusions() {
 3546   		return entityMetamodel.getPropertyUpdateGenerationInclusions();
 3547   	}
 3548   
 3549   	public boolean[] getPropertyNullability() {
 3550   		return entityMetamodel.getPropertyNullability();
 3551   	}
 3552   
 3553   	public boolean[] getPropertyVersionability() {
 3554   		return entityMetamodel.getPropertyVersionability();
 3555   	}
 3556   
 3557   	public CascadeStyle[] getPropertyCascadeStyles() {
 3558   		return entityMetamodel.getCascadeStyles();
 3559   	}
 3560   
 3561   	public final Class getMappedClass(EntityMode entityMode) {
 3562   		Tuplizer tup = entityMetamodel.getTuplizerOrNull(entityMode);
 3563   		return tup==null ? null : tup.getMappedClass();
 3564   	}
 3565   
 3566   	public boolean implementsLifecycle(EntityMode entityMode) {
 3567   		return getTuplizer( entityMode ).isLifecycleImplementor();
 3568   	}
 3569   
 3570   	public boolean implementsValidatable(EntityMode entityMode) {
 3571   		return getTuplizer( entityMode ).isValidatableImplementor();
 3572   	}
 3573   
 3574   	public Class getConcreteProxyClass(EntityMode entityMode) {
 3575   		return getTuplizer( entityMode ).getConcreteProxyClass();
 3576   	}
 3577   
 3578   	public void setPropertyValues(Object object, Object[] values, EntityMode entityMode)
 3579   			throws HibernateException {
 3580   		getTuplizer( entityMode ).setPropertyValues( object, values );
 3581   	}
 3582   
 3583   	public void setPropertyValue(Object object, int i, Object value, EntityMode entityMode)
 3584   			throws HibernateException {
 3585   		getTuplizer( entityMode ).setPropertyValue( object, i, value );
 3586   	}
 3587   
 3588   	public Object[] getPropertyValues(Object object, EntityMode entityMode)
 3589   			throws HibernateException {
 3590   		return getTuplizer( entityMode ).getPropertyValues( object );
 3591   	}
 3592   
 3593   	public Object getPropertyValue(Object object, int i, EntityMode entityMode)
 3594   			throws HibernateException {
 3595   		return getTuplizer( entityMode ).getPropertyValue( object , i );
 3596   	}
 3597   
 3598   	public Object getPropertyValue(Object object, String propertyName, EntityMode entityMode)
 3599   			throws HibernateException {
 3600   		return getTuplizer( entityMode ).getPropertyValue( object, propertyName );
 3601   	}
 3602   
 3603   	public Serializable getIdentifier(Object object, EntityMode entityMode)
 3604   			throws HibernateException {
 3605   		return getTuplizer( entityMode ).getIdentifier( object );
 3606   	}
 3607   
 3608   	public void setIdentifier(Object object, Serializable id, EntityMode entityMode)
 3609   			throws HibernateException {
 3610   		getTuplizer( entityMode ).setIdentifier( object, id );
 3611   	}
 3612   
 3613   	public Object getVersion(Object object, EntityMode entityMode)
 3614   			throws HibernateException {
 3615   		return getTuplizer( entityMode ).getVersion( object );
 3616   	}
 3617   
 3618   	public Object instantiate(Serializable id, EntityMode entityMode)
 3619   			throws HibernateException {
 3620   		return getTuplizer( entityMode ).instantiate( id );
 3621   	}
 3622   
 3623   	public boolean isInstance(Object object, EntityMode entityMode) {
 3624   		return getTuplizer( entityMode ).isInstance( object );
 3625   	}
 3626   
 3627   	public boolean hasUninitializedLazyProperties(Object object, EntityMode entityMode) {
 3628   		return getTuplizer( entityMode ).hasUninitializedLazyProperties( object );
 3629   	}
 3630   
 3631   	public void resetIdentifier(Object entity, Serializable currentId, Object currentVersion, EntityMode entityMode) {
 3632   		getTuplizer( entityMode ).resetIdentifier( entity, currentId, currentVersion );
 3633   	}
 3634   
 3635   	public EntityPersister getSubclassEntityPersister(
 3636   			Object instance,
 3637   			SessionFactoryImplementor factory,
 3638   			EntityMode entityMode) {
 3639   		if ( !hasSubclasses() ) {
 3640   			return this;
 3641   		}
 3642   		else {
 3643   			final String concreteEntityName = getTuplizer( entityMode )
 3644   					.determineConcreteSubclassEntityName( instance, factory );
 3645   			if ( concreteEntityName == null || getEntityName().equals( concreteEntityName ) ) {
 3646   				// the contract of EntityTuplizer.determineConcreteSubclassEntityName says that returning null
 3647   				// is an indication that the specified entity-name (this.getEntityName) should be used.
 3648   				return this;
 3649   			}
 3650   			else {
 3651   				return factory.getEntityPersister( concreteEntityName );
 3652   			}
 3653   		}
 3654   	}
 3655   
 3656   	public EntityMode guessEntityMode(Object object) {
 3657   		return entityMetamodel.guessEntityMode(object);
 3658   	}
 3659   
 3660   	public boolean isMultiTable() {
 3661   		return false;
 3662   	}
 3663   
 3664   	public String getTemporaryIdTableName() {
 3665   		return temporaryIdTableName;
 3666   	}
 3667   
 3668   	public String getTemporaryIdTableDDL() {
 3669   		return temporaryIdTableDDL;
 3670   	}
 3671   
 3672   	protected int getPropertySpan() {
 3673   		return entityMetamodel.getPropertySpan();
 3674   	}
 3675   
 3676   	public Object[] getPropertyValuesToInsert(Object object, Map mergeMap, SessionImplementor session) throws HibernateException {
 3677   		return getTuplizer( session.getEntityMode() ).getPropertyValuesToInsert( object, mergeMap, session );
 3678   	}
 3679   
 3680   	public void processInsertGeneratedProperties(Serializable id, Object entity, Object[] state, SessionImplementor session) {
 3681   		if ( !hasInsertGeneratedProperties() ) {
 3682   			throw new AssertionFailure("no insert-generated properties");
 3683   		}
 3684   		processGeneratedProperties( id, entity, state, session, sqlInsertGeneratedValuesSelectString, getPropertyInsertGenerationInclusions() );
 3685   	}
 3686   
 3687   	public void processUpdateGeneratedProperties(Serializable id, Object entity, Object[] state, SessionImplementor session) {
 3688   		if ( !hasUpdateGeneratedProperties() ) {
 3689   			throw new AssertionFailure("no update-generated properties");
 3690   		}
 3691   		processGeneratedProperties( id, entity, state, session, sqlUpdateGeneratedValuesSelectString, getPropertyUpdateGenerationInclusions() );
 3692   	}
 3693   
 3694   	private void processGeneratedProperties(
 3695   			Serializable id,
 3696   	        Object entity,
 3697   	        Object[] state,
 3698   	        SessionImplementor session,
 3699   	        String selectionSQL,
 3700   	        ValueInclusion[] includeds) {
 3701   
 3702   		session.getBatcher().executeBatch(); //force immediate execution of the insert
 3703   
 3704   		try {
 3705   			PreparedStatement ps = session.getBatcher().prepareSelectStatement( selectionSQL );
 3706   			try {
 3707   				getIdentifierType().nullSafeSet( ps, id, 1, session );
 3708   				ResultSet rs = ps.executeQuery();
 3709   				try {
 3710   					if ( !rs.next() ) {
 3711   						throw new HibernateException(
 3712   								"Unable to locate row for retrieval of generated properties: " +
 3713   								MessageHelper.infoString( this, id, getFactory() )
 3714   							);
 3715   					}
 3716   					for ( int i = 0; i < getPropertySpan(); i++ ) {
 3717   						if ( includeds[i] != ValueInclusion.NONE ) {
 3718   							Object hydratedState = getPropertyTypes()[i].hydrate( rs, getPropertyAliases( "", i ), session, entity );
 3719   							state[i] = getPropertyTypes()[i].resolve( hydratedState, session, entity );
 3720   							setPropertyValue( entity, i, state[i], session.getEntityMode() );
 3721   						}
 3722   					}
 3723   				}
 3724   				finally {
 3725   					if ( rs != null ) {
 3726   						rs.close();
 3727   					}
 3728   				}
 3729   			}
 3730   			finally {
 3731   				session.getBatcher().closeStatement( ps );
 3732   			}
 3733   		}
 3734   		catch( SQLException sqle ) {
 3735   			throw JDBCExceptionHelper.convert(
 3736   					getFactory().getSQLExceptionConverter(),
 3737   					sqle,
 3738   					"unable to select generated column values",
 3739   					selectionSQL
 3740   			);
 3741   		}
 3742   
 3743   	}
 3744   
 3745   	public String getIdentifierPropertyName() {
 3746   		return entityMetamodel.getIdentifierProperty().getName();
 3747   	}
 3748   
 3749   	public Type getIdentifierType() {
 3750   		return entityMetamodel.getIdentifierProperty().getType();
 3751   	}
 3752   
 3753   	public boolean hasSubselectLoadableCollections() {
 3754   		return hasSubselectLoadableCollections;
 3755   	}
 3756   
 3757   	public int[] getNaturalIdentifierProperties() {
 3758   		return entityMetamodel.getNaturalIdentifierProperties();
 3759   	}
 3760   
 3761   	public Object[] getNaturalIdentifierSnapshot(Serializable id, SessionImplementor session) throws HibernateException {
 3762   		if ( !hasNaturalIdentifier() ) {
 3763   			throw new MappingException( "persistent class did not define a natural-id : " + MessageHelper.infoString( this ) );
 3764   		}
 3765   		if ( log.isTraceEnabled() ) {
 3766   			log.trace( "Getting current natural-id snapshot state for: " + MessageHelper.infoString( this, id, getFactory() ) );
 3767   		}
 3768   
 3769   		int[] naturalIdPropertyIndexes = getNaturalIdentifierProperties();
 3770   		int naturalIdPropertyCount = naturalIdPropertyIndexes.length;
 3771   		boolean[] naturalIdMarkers = new boolean[ getPropertySpan() ];
 3772   		Type[] extractionTypes = new Type[ naturalIdPropertyCount ];
 3773   		for ( int i = 0; i < naturalIdPropertyCount; i++ ) {
 3774   			extractionTypes[i] = getPropertyTypes()[ naturalIdPropertyIndexes[i] ];
 3775   			naturalIdMarkers[ naturalIdPropertyIndexes[i] ] = true;
 3776   		}
 3777   
 3778   		///////////////////////////////////////////////////////////////////////
 3779   		// TODO : look at perhaps caching this...
 3780   		Select select = new Select( getFactory().getDialect() );
 3781   		if ( getFactory().getSettings().isCommentsEnabled() ) {
 3782   			select.setComment( "get current natural-id state " + getEntityName() );
 3783   		}
 3784   		select.setSelectClause( concretePropertySelectFragmentSansLeadingComma( getRootAlias(), naturalIdMarkers ) );
 3785   		select.setFromClause( fromTableFragment( getRootAlias() ) + fromJoinFragment( getRootAlias(), true, false ) );
 3786   
 3787   		String[] aliasedIdColumns = StringHelper.qualify( getRootAlias(), getIdentifierColumnNames() );
 3788   		String whereClause = new StringBuffer()
 3789   			.append( StringHelper.join( "=? and ",
 3790   					aliasedIdColumns ) )
 3791   			.append( "=?" )
 3792   			.append( whereJoinFragment( getRootAlias(), true, false ) )
 3793   			.toString();
 3794   
 3795   		String sql = select.setOuterJoins( "", "" )
 3796   				.setWhereClause( whereClause )
 3797   				.toStatementString();
 3798   		///////////////////////////////////////////////////////////////////////
 3799   
 3800   		Object[] snapshot = new Object[ naturalIdPropertyCount ];
 3801   		try {
 3802   			PreparedStatement ps = session.getBatcher().prepareSelectStatement( sql );
 3803   			try {
 3804   				getIdentifierType().nullSafeSet( ps, id, 1, session );
 3805   				ResultSet rs = ps.executeQuery();
 3806   				try {
 3807   					//if there is no resulting row, return null
 3808   					if ( !rs.next() ) {
 3809   						return null;
 3810   					}
 3811   
 3812   					for ( int i = 0; i < naturalIdPropertyCount; i++ ) {
 3813   						snapshot[i] = extractionTypes[i].hydrate( rs, getPropertyAliases( "", naturalIdPropertyIndexes[i] ), session, null );
 3814   					}
 3815   					return snapshot;
 3816   				}
 3817   				finally {
 3818   					rs.close();
 3819   				}
 3820   			}
 3821   			finally {
 3822   				session.getBatcher().closeStatement( ps );
 3823   			}
 3824   		}
 3825   		catch ( SQLException sqle ) {
 3826   			throw JDBCExceptionHelper.convert(
 3827   					getFactory().getSQLExceptionConverter(),
 3828   					sqle,
 3829   					"could not retrieve snapshot: " +
 3830   					MessageHelper.infoString( this, id, getFactory() ),
 3831   			        sql
 3832   				);
 3833   		}
 3834   	}
 3835   
 3836   	protected String concretePropertySelectFragmentSansLeadingComma(String alias, boolean[] include) {
 3837   		String concretePropertySelectFragment = concretePropertySelectFragment( alias, include );
 3838   		int firstComma = concretePropertySelectFragment.indexOf( ", " );
 3839   		if ( firstComma == 0 ) {
 3840   			concretePropertySelectFragment = concretePropertySelectFragment.substring( 2 );
 3841   		}
 3842   		return concretePropertySelectFragment;
 3843   	}
 3844   	public boolean hasNaturalIdentifier() {
 3845   		return entityMetamodel.hasNaturalIdentifier();
 3846   	}
 3847   
 3848   	public void setPropertyValue(Object object, String propertyName, Object value, EntityMode entityMode)
 3849   			throws HibernateException {
 3850   		getTuplizer( entityMode ).setPropertyValue( object, propertyName, value );
 3851   	}
 3852   	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 3853   
 3854   }

Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » persister » entity » [javadoc | source]