Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » persister » collection » [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.collection;
   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.Arrays;
   32   import java.util.HashMap;
   33   import java.util.Iterator;
   34   import java.util.Map;
   35   
   36   import org.slf4j.Logger;
   37   import org.slf4j.LoggerFactory;
   38   import org.hibernate.AssertionFailure;
   39   import org.hibernate.FetchMode;
   40   import org.hibernate.HibernateException;
   41   import org.hibernate.MappingException;
   42   import org.hibernate.QueryException;
   43   import org.hibernate.TransientObjectException;
   44   import org.hibernate.jdbc.Expectation;
   45   import org.hibernate.jdbc.Expectations;
   46   import org.hibernate.cache.CacheException;
   47   import org.hibernate.cache.access.CollectionRegionAccessStrategy;
   48   import org.hibernate.cache.entry.CacheEntryStructure;
   49   import org.hibernate.cache.entry.StructuredCollectionCacheEntry;
   50   import org.hibernate.cache.entry.StructuredMapCacheEntry;
   51   import org.hibernate.cache.entry.UnstructuredCacheEntry;
   52   import org.hibernate.cfg.Configuration;
   53   import org.hibernate.collection.PersistentCollection;
   54   import org.hibernate.dialect.Dialect;
   55   import org.hibernate.engine.EntityKey;
   56   import org.hibernate.engine.PersistenceContext;
   57   import org.hibernate.engine.SessionFactoryImplementor;
   58   import org.hibernate.engine.SessionImplementor;
   59   import org.hibernate.engine.SubselectFetch;
   60   import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
   61   import org.hibernate.exception.JDBCExceptionHelper;
   62   import org.hibernate.exception.SQLExceptionConverter;
   63   import org.hibernate.id.IdentifierGenerator;
   64   import org.hibernate.loader.collection.CollectionInitializer;
   65   import org.hibernate.mapping.Collection;
   66   import org.hibernate.mapping.Column;
   67   import org.hibernate.mapping.Formula;
   68   import org.hibernate.mapping.IdentifierCollection;
   69   import org.hibernate.mapping.IndexedCollection;
   70   import org.hibernate.mapping.List;
   71   import org.hibernate.mapping.Selectable;
   72   import org.hibernate.mapping.Table;
   73   import org.hibernate.metadata.CollectionMetadata;
   74   import org.hibernate.persister.entity.EntityPersister;
   75   import org.hibernate.persister.entity.Loadable;
   76   import org.hibernate.persister.entity.PropertyMapping;
   77   import org.hibernate.pretty.MessageHelper;
   78   import org.hibernate.sql.Alias;
   79   import org.hibernate.sql.SelectFragment;
   80   import org.hibernate.sql.SimpleSelect;
   81   import org.hibernate.sql.Template;
   82   import org.hibernate.type.AbstractComponentType;
   83   import org.hibernate.type.CollectionType;
   84   import org.hibernate.type.EntityType;
   85   import org.hibernate.type.Type;
   86   import org.hibernate.util.ArrayHelper;
   87   import org.hibernate.util.CollectionHelper;
   88   import org.hibernate.util.FilterHelper;
   89   import org.hibernate.util.StringHelper;
   90   
   91   
   92   /**
   93    * Base implementation of the <tt>QueryableCollection</tt> interface.
   94    *
   95    * @author Gavin King
   96    * @see BasicCollectionPersister
   97    * @see OneToManyPersister
   98    */
   99   public abstract class AbstractCollectionPersister
  100   		implements CollectionMetadata, SQLLoadableCollection {
  101   	// TODO: encapsulate the protected instance variables!
  102   
  103   	private final String role;
  104   
  105   	//SQL statements
  106   	private final String sqlDeleteString;
  107   	private final String sqlInsertRowString;
  108   	private final String sqlUpdateRowString;
  109   	private final String sqlDeleteRowString;
  110   	private final String sqlSelectSizeString;
  111   	private final String sqlSelectRowByIndexString;
  112   	private final String sqlDetectRowByIndexString;
  113   	private final String sqlDetectRowByElementString;
  114   
  115   	private final String sqlOrderByString;
  116   	protected final String sqlWhereString;
  117   	private final String sqlOrderByStringTemplate;
  118   	private final String sqlWhereStringTemplate;
  119   	private final boolean hasOrder;
  120   	protected final boolean hasWhere;
  121   	private final int baseIndex;
  122   	
  123   	private final String nodeName;
  124   	private final String elementNodeName;
  125   	private final String indexNodeName;
  126   
  127   	protected final boolean indexContainsFormula;
  128   	protected final boolean elementIsPureFormula;
  129   	
  130   	//types
  131   	private final Type keyType;
  132   	private final Type indexType;
  133   	protected final Type elementType;
  134   	private final Type identifierType;
  135   
  136   	//columns
  137   	protected final String[] keyColumnNames;
  138   	protected final String[] indexColumnNames;
  139   	protected final String[] indexFormulaTemplates;
  140   	protected final String[] indexFormulas;
  141   	protected final boolean[] indexColumnIsSettable;
  142   	protected final String[] elementColumnNames;
  143   	protected final String[] elementFormulaTemplates;
  144   	protected final String[] elementFormulas;
  145   	protected final boolean[] elementColumnIsSettable;
  146   	protected final boolean[] elementColumnIsInPrimaryKey;
  147   	protected final String[] indexColumnAliases;
  148   	protected final String[] elementColumnAliases;
  149   	protected final String[] keyColumnAliases;
  150   	
  151   	protected final String identifierColumnName;
  152   	private final String identifierColumnAlias;
  153   	//private final String unquotedIdentifierColumnName;
  154   
  155   	protected final String qualifiedTableName;
  156   
  157   	private final String queryLoaderName;
  158   
  159   	private final boolean isPrimitiveArray;
  160   	private final boolean isArray;
  161   	protected final boolean hasIndex;
  162   	protected final boolean hasIdentifier;
  163   	private final boolean isLazy;
  164   	private final boolean isExtraLazy;
  165   	private final boolean isInverse;
  166   	private final boolean isMutable;
  167   	private final boolean isVersioned;
  168   	protected final int batchSize;
  169   	private final FetchMode fetchMode;
  170   	private final boolean hasOrphanDelete;
  171   	private final boolean subselectLoadable;
  172   
  173   	//extra information about the element type
  174   	private final Class elementClass;
  175   	private final String entityName;
  176   
  177   	private final Dialect dialect;
  178   	private final SQLExceptionConverter sqlExceptionConverter;
  179   	private final SessionFactoryImplementor factory;
  180   	private final EntityPersister ownerPersister;
  181   	private final IdentifierGenerator identifierGenerator;
  182   	private final PropertyMapping elementPropertyMapping;
  183   	private final EntityPersister elementPersister;
  184   	private final CollectionRegionAccessStrategy cacheAccessStrategy;
  185   	private final CollectionType collectionType;
  186   	private CollectionInitializer initializer;
  187   	
  188   	private final CacheEntryStructure cacheEntryStructure;
  189   
  190   	// dynamic filters for the collection
  191   	private final FilterHelper filterHelper;
  192   
  193   	// dynamic filters specifically for many-to-many inside the collection
  194   	private final FilterHelper manyToManyFilterHelper;
  195   
  196   	private final String manyToManyWhereString;
  197   	private final String manyToManyWhereTemplate;
  198   
  199   	private final String manyToManyOrderByString;
  200   	private final String manyToManyOrderByTemplate;
  201   
  202   	// custom sql
  203   	private final boolean insertCallable;
  204   	private final boolean updateCallable;
  205   	private final boolean deleteCallable;
  206   	private final boolean deleteAllCallable;
  207   	private ExecuteUpdateResultCheckStyle insertCheckStyle;
  208   	private ExecuteUpdateResultCheckStyle updateCheckStyle;
  209   	private ExecuteUpdateResultCheckStyle deleteCheckStyle;
  210   	private ExecuteUpdateResultCheckStyle deleteAllCheckStyle;
  211   
  212   	private final Serializable[] spaces;
  213   
  214   	private Map collectionPropertyColumnAliases = new HashMap();
  215   	private Map collectionPropertyColumnNames = new HashMap();
  216   
  217   	private static final Logger log = LoggerFactory.getLogger( AbstractCollectionPersister.class );
  218   
  219   	public AbstractCollectionPersister(
  220   			final Collection collection,
  221   			final CollectionRegionAccessStrategy cacheAccessStrategy,
  222   			final Configuration cfg,
  223   			final SessionFactoryImplementor factory) throws MappingException, CacheException {
  224   
  225   		this.factory = factory;
  226   		this.cacheAccessStrategy = cacheAccessStrategy;
  227   		if ( factory.getSettings().isStructuredCacheEntriesEnabled() ) {
  228   			cacheEntryStructure = collection.isMap() ?
  229   					( CacheEntryStructure ) new StructuredMapCacheEntry() :
  230   					( CacheEntryStructure ) new StructuredCollectionCacheEntry();
  231   		}
  232   		else {
  233   			cacheEntryStructure = new UnstructuredCacheEntry();
  234   		}
  235   		
  236   		dialect = factory.getDialect();
  237   		sqlExceptionConverter = factory.getSQLExceptionConverter();
  238   		collectionType = collection.getCollectionType();
  239   		role = collection.getRole();
  240   		entityName = collection.getOwnerEntityName();
  241   		ownerPersister = factory.getEntityPersister(entityName);
  242   		queryLoaderName = collection.getLoaderName();
  243   		nodeName = collection.getNodeName();
  244   		isMutable = collection.isMutable();
  245   
  246   		Table table = collection.getCollectionTable();
  247   		fetchMode = collection.getElement().getFetchMode();
  248   		elementType = collection.getElement().getType();
  249   		//isSet = collection.isSet();
  250   		//isSorted = collection.isSorted();
  251   		isPrimitiveArray = collection.isPrimitiveArray();
  252   		isArray = collection.isArray();
  253   		subselectLoadable = collection.isSubselectLoadable();
  254   		
  255   		qualifiedTableName = table.getQualifiedName( 
  256   				dialect,
  257   				factory.getSettings().getDefaultCatalogName(),
  258   				factory.getSettings().getDefaultSchemaName() 
  259   			);
  260   
  261   		int spacesSize = 1 + collection.getSynchronizedTables().size();
  262   		spaces = new String[spacesSize];
  263   		spaces[0] = qualifiedTableName;
  264   		Iterator iter = collection.getSynchronizedTables().iterator();
  265   		for ( int i = 1; i < spacesSize; i++ ) {
  266   			spaces[i] = (String) iter.next();
  267   		}
  268   		
  269   		sqlOrderByString = collection.getOrderBy();
  270   		hasOrder = sqlOrderByString != null;
  271   		sqlOrderByStringTemplate = hasOrder ?
  272   				Template.renderOrderByStringTemplate(sqlOrderByString, dialect, factory.getSqlFunctionRegistry()) :
  273   				null;
  274   		sqlWhereString = StringHelper.isNotEmpty( collection.getWhere() ) ? "( " + collection.getWhere() + ") " : null;
  275   		hasWhere = sqlWhereString != null;
  276   		sqlWhereStringTemplate = hasWhere ?
  277   				Template.renderWhereStringTemplate(sqlWhereString, dialect, factory.getSqlFunctionRegistry()) :
  278   				null;
  279   
  280   		hasOrphanDelete = collection.hasOrphanDelete();
  281   
  282   		int batch = collection.getBatchSize();
  283   		if ( batch == -1 ) {
  284   			batch = factory.getSettings().getDefaultBatchFetchSize();
  285   		}
  286   		batchSize = batch;
  287   
  288   		isVersioned = collection.isOptimisticLocked();
  289   		
  290   		// KEY
  291   
  292   		keyType = collection.getKey().getType();
  293   		iter = collection.getKey().getColumnIterator();
  294   		int keySpan = collection.getKey().getColumnSpan();
  295   		keyColumnNames = new String[keySpan];
  296   		keyColumnAliases = new String[keySpan];
  297   		int k = 0;
  298   		while ( iter.hasNext() ) {
  299   			// NativeSQL: collect key column and auto-aliases
  300   			Column col = ( (Column) iter.next() );
  301   			keyColumnNames[k] = col.getQuotedName(dialect);
  302   			keyColumnAliases[k] = col.getAlias(dialect);
  303   			k++;
  304   		}
  305   		
  306   		//unquotedKeyColumnNames = StringHelper.unQuote(keyColumnAliases);
  307   
  308   		//ELEMENT
  309   
  310   		String elemNode = collection.getElementNodeName();
  311   		if ( elementType.isEntityType() ) {
  312   			String entityName = ( (EntityType) elementType ).getAssociatedEntityName();
  313   			elementPersister = factory.getEntityPersister(entityName);
  314   			if ( elemNode==null ) {
  315   				elemNode = cfg.getClassMapping(entityName).getNodeName();
  316   			}
  317   			// NativeSQL: collect element column and auto-aliases
  318   			
  319   		}
  320   		else {
  321   			elementPersister = null;
  322   		}		
  323   		elementNodeName = elemNode;
  324   
  325   		int elementSpan = collection.getElement().getColumnSpan();
  326   		elementColumnAliases = new String[elementSpan];
  327   		elementColumnNames = new String[elementSpan];
  328   		elementFormulaTemplates = new String[elementSpan];
  329   		elementFormulas = new String[elementSpan];
  330   		elementColumnIsSettable = new boolean[elementSpan];
  331   		elementColumnIsInPrimaryKey = new boolean[elementSpan];
  332   		boolean isPureFormula = true;
  333   		boolean hasNotNullableColumns = false;
  334   		int j = 0;
  335   		iter = collection.getElement().getColumnIterator();
  336   		while ( iter.hasNext() ) {
  337   			Selectable selectable = (Selectable) iter.next();
  338   			elementColumnAliases[j] = selectable.getAlias(dialect);
  339   			if ( selectable.isFormula() ) {
  340   				Formula form = (Formula) selectable;
  341   				elementFormulaTemplates[j] = form.getTemplate(dialect, factory.getSqlFunctionRegistry());
  342   				elementFormulas[j] = form.getFormula();
  343   			}
  344   			else {
  345   				Column col = (Column) selectable;
  346   				elementColumnNames[j] = col.getQuotedName(dialect);
  347   				elementColumnIsSettable[j] = true;
  348   				elementColumnIsInPrimaryKey[j] = !col.isNullable();
  349   				if ( !col.isNullable() ) {
  350   					hasNotNullableColumns = true;
  351   				}
  352   				isPureFormula = false;
  353   			}
  354   			j++;
  355   		}
  356   		elementIsPureFormula = isPureFormula;
  357   		
  358   		//workaround, for backward compatibility of sets with no
  359   		//not-null columns, assume all columns are used in the
  360   		//row locator SQL
  361   		if ( !hasNotNullableColumns ) {
  362   			Arrays.fill( elementColumnIsInPrimaryKey, true );
  363   		}
  364   
  365   
  366   		// INDEX AND ROW SELECT
  367   
  368   		hasIndex = collection.isIndexed();
  369   		if (hasIndex) {
  370   			// NativeSQL: collect index column and auto-aliases
  371   			IndexedCollection indexedCollection = (IndexedCollection) collection;
  372   			indexType = indexedCollection.getIndex().getType();
  373   			int indexSpan = indexedCollection.getIndex().getColumnSpan();
  374   			iter = indexedCollection.getIndex().getColumnIterator();
  375   			indexColumnNames = new String[indexSpan];
  376   			indexFormulaTemplates = new String[indexSpan];
  377   			indexFormulas = new String[indexSpan];
  378   			indexColumnIsSettable = new boolean[indexSpan];
  379   			indexColumnAliases = new String[indexSpan];
  380   			int i = 0;
  381   			boolean hasFormula = false;
  382   			while ( iter.hasNext() ) {
  383   				Selectable s = (Selectable) iter.next();
  384   				indexColumnAliases[i] = s.getAlias(dialect);
  385   				if ( s.isFormula() ) {
  386   					Formula indexForm = (Formula) s;
  387   					indexFormulaTemplates[i] = indexForm.getTemplate(dialect, factory.getSqlFunctionRegistry());
  388   					indexFormulas[i] = indexForm.getFormula();
  389   					hasFormula = true;
  390   				}
  391   				else {
  392   					Column indexCol = (Column) s;
  393   					indexColumnNames[i] = indexCol.getQuotedName(dialect);
  394   					indexColumnIsSettable[i] = true;
  395   				}
  396   				i++;
  397   			}
  398   			indexContainsFormula = hasFormula;
  399   			baseIndex = indexedCollection.isList() ? 
  400   					( (List) indexedCollection ).getBaseIndex() : 0;
  401   
  402   			indexNodeName = indexedCollection.getIndexNodeName(); 
  403   
  404   		}
  405   		else {
  406   			indexContainsFormula = false;
  407   			indexColumnIsSettable = null;
  408   			indexFormulaTemplates = null;
  409   			indexFormulas = null;
  410   			indexType = null;
  411   			indexColumnNames = null;
  412   			indexColumnAliases = null;
  413   			baseIndex = 0;
  414   			indexNodeName = null;
  415   		}
  416   		
  417   		hasIdentifier = collection.isIdentified();
  418   		if (hasIdentifier) {
  419   			if ( collection.isOneToMany() ) {
  420   				throw new MappingException( "one-to-many collections with identifiers are not supported" );
  421   			}
  422   			IdentifierCollection idColl = (IdentifierCollection) collection;
  423   			identifierType = idColl.getIdentifier().getType();
  424   			iter = idColl.getIdentifier().getColumnIterator();
  425   			Column col = ( Column ) iter.next();
  426   			identifierColumnName = col.getQuotedName(dialect);
  427   			identifierColumnAlias = col.getAlias(dialect);
  428   			//unquotedIdentifierColumnName = identifierColumnAlias;
  429   			identifierGenerator = idColl.getIdentifier().createIdentifierGenerator( 
  430   					factory.getDialect(),
  431   					factory.getSettings().getDefaultCatalogName(),
  432   					factory.getSettings().getDefaultSchemaName(),
  433   					null
  434   				);
  435   		}
  436   		else {
  437   			identifierType = null;
  438   			identifierColumnName = null;
  439   			identifierColumnAlias = null;
  440   			//unquotedIdentifierColumnName = null;
  441   			identifierGenerator = null;
  442   		}
  443   		
  444   		//GENERATE THE SQL:
  445   				
  446   		//sqlSelectString = sqlSelectString();
  447   		//sqlSelectRowString = sqlSelectRowString();
  448   
  449   		if ( collection.getCustomSQLInsert() == null ) {
  450   			sqlInsertRowString = generateInsertRowString();
  451   			insertCallable = false;
  452   			insertCheckStyle = ExecuteUpdateResultCheckStyle.COUNT;
  453   		}
  454   		else {
  455   			sqlInsertRowString = collection.getCustomSQLInsert();
  456   			insertCallable = collection.isCustomInsertCallable();
  457   			insertCheckStyle = collection.getCustomSQLInsertCheckStyle() == null
  458   					? ExecuteUpdateResultCheckStyle.determineDefault( collection.getCustomSQLInsert(), insertCallable )
  459   		            : collection.getCustomSQLInsertCheckStyle();
  460   		}
  461   
  462   		if ( collection.getCustomSQLUpdate() == null ) {
  463   			sqlUpdateRowString = generateUpdateRowString();
  464   			updateCallable = false;
  465   			updateCheckStyle = ExecuteUpdateResultCheckStyle.COUNT;
  466   		}
  467   		else {
  468   			sqlUpdateRowString = collection.getCustomSQLUpdate();
  469   			updateCallable = collection.isCustomUpdateCallable();
  470   			updateCheckStyle = collection.getCustomSQLUpdateCheckStyle() == null
  471   					? ExecuteUpdateResultCheckStyle.determineDefault( collection.getCustomSQLUpdate(), insertCallable )
  472   		            : collection.getCustomSQLUpdateCheckStyle();
  473   		}
  474   
  475   		if ( collection.getCustomSQLDelete() == null ) {
  476   			sqlDeleteRowString = generateDeleteRowString();
  477   			deleteCallable = false;
  478   			deleteCheckStyle = ExecuteUpdateResultCheckStyle.NONE;
  479   		}
  480   		else {
  481   			sqlDeleteRowString = collection.getCustomSQLDelete();
  482   			deleteCallable = collection.isCustomDeleteCallable();
  483   			deleteCheckStyle = ExecuteUpdateResultCheckStyle.NONE;
  484   		}
  485   
  486   		if ( collection.getCustomSQLDeleteAll() == null ) {
  487   			sqlDeleteString = generateDeleteString();
  488   			deleteAllCallable = false;
  489   			deleteAllCheckStyle = ExecuteUpdateResultCheckStyle.NONE;
  490   		}
  491   		else {
  492   			sqlDeleteString = collection.getCustomSQLDeleteAll();
  493   			deleteAllCallable = collection.isCustomDeleteAllCallable();
  494   			deleteAllCheckStyle = ExecuteUpdateResultCheckStyle.NONE;
  495   		}
  496   
  497   		sqlSelectSizeString = generateSelectSizeString(  collection.isIndexed() && !collection.isMap() );
  498   		sqlDetectRowByIndexString = generateDetectRowByIndexString();
  499   		sqlDetectRowByElementString = generateDetectRowByElementString();
  500   		sqlSelectRowByIndexString = generateSelectRowByIndexString();
  501   		
  502   		logStaticSQL();
  503   		
  504   		isLazy = collection.isLazy();
  505   		isExtraLazy = collection.isExtraLazy();
  506   
  507   		isInverse = collection.isInverse();
  508   
  509   		if ( collection.isArray() ) {
  510   			elementClass = ( (org.hibernate.mapping.Array) collection ).getElementClass();
  511   		}
  512   		else {
  513   			// for non-arrays, we don't need to know the element class
  514   			elementClass = null; //elementType.returnedClass();
  515   		}
  516   
  517   		if ( elementType.isComponentType() ) {
  518   			elementPropertyMapping = new CompositeElementPropertyMapping( 
  519   					elementColumnNames,
  520   					elementFormulaTemplates,
  521   					(AbstractComponentType) elementType,
  522   					factory 
  523   				);
  524   		}
  525   		else if ( !elementType.isEntityType() ) {
  526   			elementPropertyMapping = new ElementPropertyMapping( 
  527   					elementColumnNames,
  528   					elementType 
  529   				);
  530   		}
  531   		else {
  532   			if ( elementPersister instanceof PropertyMapping ) { //not all classpersisters implement PropertyMapping!
  533   				elementPropertyMapping = (PropertyMapping) elementPersister;
  534   			}
  535   			else {
  536   				elementPropertyMapping = new ElementPropertyMapping( 
  537   						elementColumnNames,
  538   						elementType 
  539   					);
  540   			}
  541   		}
  542   			
  543   		// Handle any filters applied to this collection
  544   		filterHelper = new FilterHelper( collection.getFilterMap(), dialect, factory.getSqlFunctionRegistry() );
  545   
  546   		// Handle any filters applied to this collection for many-to-many
  547   		manyToManyFilterHelper = new FilterHelper( collection.getManyToManyFilterMap(), dialect, factory.getSqlFunctionRegistry() );
  548   		manyToManyWhereString = StringHelper.isNotEmpty( collection.getManyToManyWhere() ) ?
  549   				"( " + collection.getManyToManyWhere() + ")" :
  550   				null;
  551   		manyToManyWhereTemplate = manyToManyWhereString == null ?
  552   				null :
  553   				Template.renderWhereStringTemplate( manyToManyWhereString, factory.getDialect(), factory.getSqlFunctionRegistry() );
  554   		manyToManyOrderByString = collection.getManyToManyOrdering();
  555   		manyToManyOrderByTemplate = manyToManyOrderByString == null
  556   				? null
  557   	            : Template.renderOrderByStringTemplate( manyToManyOrderByString, factory.getDialect(), factory.getSqlFunctionRegistry() );
  558   
  559   		initCollectionPropertyMap();
  560   	}
  561   
  562   	public void postInstantiate() throws MappingException {
  563   		initializer = queryLoaderName == null ?
  564   				createCollectionInitializer( CollectionHelper.EMPTY_MAP ) :
  565   				new NamedQueryCollectionInitializer( queryLoaderName, this );
  566   	}
  567   
  568   	protected void logStaticSQL() {
  569   		if ( log.isDebugEnabled() ) {
  570   			log.debug( "Static SQL for collection: " + getRole() );
  571   			if ( getSQLInsertRowString() != null ) {
  572   				log.debug( " Row insert: " + getSQLInsertRowString() );
  573   			}
  574   			if ( getSQLUpdateRowString() != null ) {
  575   				log.debug( " Row update: " + getSQLUpdateRowString() );
  576   			}
  577   			if ( getSQLDeleteRowString() != null ) {
  578   				log.debug( " Row delete: " + getSQLDeleteRowString() );
  579   			}
  580   			if ( getSQLDeleteString() != null ) {
  581   				log.debug( " One-shot delete: " + getSQLDeleteString() );
  582   			}
  583   		}
  584   	}
  585   
  586   	public void initialize(Serializable key, SessionImplementor session) throws HibernateException {
  587   		getAppropriateInitializer( key, session ).initialize( key, session );
  588   	}
  589   
  590   	protected CollectionInitializer getAppropriateInitializer(Serializable key, SessionImplementor session) {
  591   		if ( queryLoaderName != null ) {
  592   			//if there is a user-specified loader, return that
  593   			//TODO: filters!?
  594   			return initializer;
  595   		}
  596   		CollectionInitializer subselectInitializer = getSubselectInitializer( key, session );
  597   		if ( subselectInitializer != null ) {
  598   			return subselectInitializer;
  599   		}
  600   		else if ( session.getEnabledFilters().isEmpty() ) {
  601   			return initializer;
  602   		}
  603   		else {
  604   			return createCollectionInitializer( session.getEnabledFilters() );
  605   		}
  606   	}
  607   
  608   	private CollectionInitializer getSubselectInitializer(Serializable key, SessionImplementor session) {
  609   
  610   		if ( !isSubselectLoadable() ) {
  611   			return null;
  612   		}
  613   		
  614   		final PersistenceContext persistenceContext = session.getPersistenceContext();
  615   		
  616   		SubselectFetch subselect = persistenceContext.getBatchFetchQueue()
  617   			.getSubselect( new EntityKey( key, getOwnerEntityPersister(), session.getEntityMode() ) );
  618   		
  619   		if (subselect == null) {
  620   			return null;
  621   		}
  622   		else {
  623   			
  624   			// Take care of any entities that might have
  625   			// been evicted!	
  626   			Iterator iter = subselect.getResult().iterator();
  627   			while ( iter.hasNext() ) {
  628   				if ( !persistenceContext.containsEntity( (EntityKey) iter.next() ) ) {
  629   					iter.remove();
  630   				}
  631   			}	
  632   			
  633   			// Run a subquery loader
  634   			return createSubselectInitializer( subselect, session );
  635   		}
  636   	}
  637   
  638   	protected abstract CollectionInitializer createSubselectInitializer(SubselectFetch subselect, SessionImplementor session);
  639   
  640   	protected abstract CollectionInitializer createCollectionInitializer(Map enabledFilters)
  641   			throws MappingException;
  642   
  643   	public CollectionRegionAccessStrategy getCacheAccessStrategy() {
  644   		return cacheAccessStrategy;
  645   	}
  646   
  647   	public boolean hasCache() {
  648   		return cacheAccessStrategy != null;
  649   	}
  650   
  651   	public CollectionType getCollectionType() {
  652   		return collectionType;
  653   	}
  654   
  655   	protected String getSQLWhereString(String alias) {
  656   		return StringHelper.replace( sqlWhereStringTemplate, Template.TEMPLATE, alias );
  657   	}
  658   
  659   	public String getSQLOrderByString(String alias) {
  660   		return hasOrdering() ? 
  661   			StringHelper.replace( sqlOrderByStringTemplate, Template.TEMPLATE, alias ) : "";
  662   	}
  663   
  664   	public String getManyToManyOrderByString(String alias) {
  665   		if ( isManyToMany() && manyToManyOrderByString != null ) {
  666   			return StringHelper.replace( manyToManyOrderByTemplate, Template.TEMPLATE, alias );
  667   		}
  668   		else {
  669   			return "";
  670   		}
  671   	}
  672   	public FetchMode getFetchMode() {
  673   		return fetchMode;
  674   	}
  675   
  676   	public boolean hasOrdering() {
  677   		return hasOrder;
  678   	}
  679   
  680   	public boolean hasManyToManyOrdering() {
  681   		return isManyToMany() && manyToManyOrderByTemplate != null;
  682   	}
  683   
  684   	public boolean hasWhere() {
  685   		return hasWhere;
  686   	}
  687   
  688   	protected String getSQLDeleteString() {
  689   		return sqlDeleteString;
  690   	}
  691   
  692   	protected String getSQLInsertRowString() {
  693   		return sqlInsertRowString;
  694   	}
  695   
  696   	protected String getSQLUpdateRowString() {
  697   		return sqlUpdateRowString;
  698   	}
  699   
  700   	protected String getSQLDeleteRowString() {
  701   		return sqlDeleteRowString;
  702   	}
  703   
  704   	public Type getKeyType() {
  705   		return keyType;
  706   	}
  707   
  708   	public Type getIndexType() {
  709   		return indexType;
  710   	}
  711   
  712   	public Type getElementType() {
  713   		return elementType;
  714   	}
  715   
  716   	/**
  717   	 * Return the element class of an array, or null otherwise
  718   	 */
  719   	public Class getElementClass() { //needed by arrays
  720   		return elementClass;
  721   	}
  722   
  723   	public Object readElement(ResultSet rs, Object owner, String[] aliases, SessionImplementor session) 
  724   	throws HibernateException, SQLException {
  725   		return getElementType().nullSafeGet( rs, aliases, session, owner );
  726   	}
  727   
  728   	public Object readIndex(ResultSet rs, String[] aliases, SessionImplementor session) 
  729   	throws HibernateException, SQLException {
  730   		Object index = getIndexType().nullSafeGet( rs, aliases, session, null );
  731   		if ( index == null ) {
  732   			throw new HibernateException( "null index column for collection: " + role );
  733   		}
  734   		index = decrementIndexByBase( index );
  735   		return index;
  736   	}
  737   
  738   	protected Object decrementIndexByBase(Object index) {
  739   		if (baseIndex!=0) {
  740   			index = new Integer( ( (Integer) index ).intValue() - baseIndex );
  741   		}
  742   		return index;
  743   	}
  744   
  745   	public Object readIdentifier(ResultSet rs, String alias, SessionImplementor session) 
  746   	throws HibernateException, SQLException {
  747   		Object id = getIdentifierType().nullSafeGet( rs, alias, session, null );
  748   		if ( id == null ) {
  749   			throw new HibernateException( "null identifier column for collection: " + role );
  750   		}
  751   		return id;
  752   	}
  753   
  754   	public Object readKey(ResultSet rs, String[] aliases, SessionImplementor session) 
  755   	throws HibernateException, SQLException {
  756   		return getKeyType().nullSafeGet( rs, aliases, session, null );
  757   	}
  758   
  759   	/**
  760   	 * Write the key to a JDBC <tt>PreparedStatement</tt>
  761   	 */
  762   	protected int writeKey(PreparedStatement st, Serializable key, int i, SessionImplementor session)
  763   			throws HibernateException, SQLException {
  764   		
  765   		if ( key == null ) {
  766   			throw new NullPointerException( "null key for collection: " + role );  //an assertion
  767   		}
  768   		getKeyType().nullSafeSet( st, key, i, session );
  769   		return i + keyColumnAliases.length;
  770   	}
  771   
  772   	/**
  773   	 * Write the element to a JDBC <tt>PreparedStatement</tt>
  774   	 */
  775   	protected int writeElement(PreparedStatement st, Object elt, int i, SessionImplementor session)
  776   			throws HibernateException, SQLException {
  777   		getElementType().nullSafeSet(st, elt, i, elementColumnIsSettable, session);
  778   		return i + ArrayHelper.countTrue(elementColumnIsSettable);
  779   
  780   	}
  781   
  782   	/**
  783   	 * Write the index to a JDBC <tt>PreparedStatement</tt>
  784   	 */
  785   	protected int writeIndex(PreparedStatement st, Object index, int i, SessionImplementor session)
  786   			throws HibernateException, SQLException {
  787   		getIndexType().nullSafeSet( st, incrementIndexByBase(index), i, indexColumnIsSettable, session );
  788   		return i + ArrayHelper.countTrue(indexColumnIsSettable);
  789   	}
  790   
  791   	protected Object incrementIndexByBase(Object index) {
  792   		if (baseIndex!=0) {
  793   			index = new Integer( ( (Integer) index ).intValue() + baseIndex );
  794   		}
  795   		return index;
  796   	}
  797   
  798   	/**
  799   	 * Write the element to a JDBC <tt>PreparedStatement</tt>
  800   	 */
  801   	protected int writeElementToWhere(PreparedStatement st, Object elt, int i, SessionImplementor session)
  802   			throws HibernateException, SQLException {
  803   		if (elementIsPureFormula) {
  804   			throw new AssertionFailure("cannot use a formula-based element in the where condition");
  805   		}
  806   		getElementType().nullSafeSet(st, elt, i, elementColumnIsInPrimaryKey, session);
  807   		return i + elementColumnAliases.length;
  808   
  809   	}
  810   
  811   	/**
  812   	 * Write the index to a JDBC <tt>PreparedStatement</tt>
  813   	 */
  814   	protected int writeIndexToWhere(PreparedStatement st, Object index, int i, SessionImplementor session)
  815   			throws HibernateException, SQLException {
  816   		if (indexContainsFormula) {
  817   			throw new AssertionFailure("cannot use a formula-based index in the where condition");
  818   		}
  819   		getIndexType().nullSafeSet( st, incrementIndexByBase(index), i, session );
  820   		return i + indexColumnAliases.length;
  821   	}
  822   
  823   	/**
  824   	 * Write the identifier to a JDBC <tt>PreparedStatement</tt>
  825   	 */
  826   	public int writeIdentifier(PreparedStatement st, Object id, int i, SessionImplementor session)
  827   			throws HibernateException, SQLException {
  828   		
  829   		getIdentifierType().nullSafeSet( st, id, i, session );
  830   		return i + 1;
  831   	}
  832   
  833   	public boolean isPrimitiveArray() {
  834   		return isPrimitiveArray;
  835   	}
  836   
  837   	public boolean isArray() {
  838   		return isArray;
  839   	}
  840   
  841   	public String[] getKeyColumnAliases(String suffix) {
  842   		return new Alias( suffix ).toAliasStrings( keyColumnAliases );
  843   	}
  844   
  845   	public String[] getElementColumnAliases(String suffix) {
  846   		return new Alias( suffix ).toAliasStrings( elementColumnAliases );
  847   	}
  848   
  849   	public String[] getIndexColumnAliases(String suffix) {
  850   		if ( hasIndex ) {
  851   			return new Alias( suffix ).toAliasStrings( indexColumnAliases );
  852   		}
  853   		else {
  854   			return null;
  855   		}
  856   	}
  857   
  858   	public String getIdentifierColumnAlias(String suffix) {
  859   		if ( hasIdentifier ) {
  860   			return new Alias( suffix ).toAliasString( identifierColumnAlias );
  861   		}
  862   		else {
  863   			return null;
  864   		}
  865   	}
  866   	
  867   	public String getIdentifierColumnName() {
  868   		if ( hasIdentifier ) {
  869   			return identifierColumnName;
  870   		} else {
  871   			return null;
  872   		}
  873   	}
  874   
  875   	/**
  876   	 * Generate a list of collection index, key and element columns
  877   	 */
  878   	public String selectFragment(String alias, String columnSuffix) {
  879   		SelectFragment frag = generateSelectFragment( alias, columnSuffix );
  880   		appendElementColumns( frag, alias );
  881   		appendIndexColumns( frag, alias );
  882   		appendIdentifierColumns( frag, alias );
  883   
  884   		return frag.toFragmentString()
  885   				.substring( 2 ); //strip leading ','
  886   	}
  887   
  888   	protected String generateSelectSizeString(boolean isIntegerIndexed) {
  889   		String selectValue = isIntegerIndexed ? 
  890   			"max(" + getIndexColumnNames()[0] + ") + 1": //lists, arrays
  891   			"count(" + getElementColumnNames()[0] + ")"; //sets, maps, bags
  892   		return new SimpleSelect(dialect)
  893   				.setTableName( getTableName() )
  894   				.addCondition( getKeyColumnNames(), "=?" )
  895   				.addColumn(selectValue)
  896   				.toStatementString();
  897   	}
  898   
  899   	protected String generateDetectRowByIndexString() {
  900   		if ( !hasIndex() ) {
  901   			return null;
  902   		}
  903   		return new SimpleSelect(dialect)
  904   				.setTableName( getTableName() )
  905   				.addCondition( getKeyColumnNames(), "=?" )
  906   				.addCondition( getIndexColumnNames(), "=?" )
  907   				.addCondition( indexFormulas, "=?" )
  908   				.addColumn("1")
  909   				.toStatementString();
  910   	}
  911   
  912   	protected String generateSelectRowByIndexString() {
  913   		if ( !hasIndex() ) {
  914   			return null;
  915   		}
  916   		return new SimpleSelect(dialect)
  917   				.setTableName( getTableName() )
  918   				.addCondition( getKeyColumnNames(), "=?" )
  919   				.addCondition( getIndexColumnNames(), "=?" )
  920   				.addCondition( indexFormulas, "=?" )
  921   				.addColumns( getElementColumnNames(), elementColumnAliases )
  922   				.addColumns( indexFormulas, indexColumnAliases )
  923   				.toStatementString();
  924   	}
  925   
  926   	protected String generateDetectRowByElementString() {
  927   		return new SimpleSelect(dialect)
  928   				.setTableName( getTableName() )
  929   				.addCondition( getKeyColumnNames(), "=?" )
  930   				.addCondition( getElementColumnNames(), "=?" )
  931   				.addCondition( elementFormulas, "=?" )
  932   				.addColumn("1")
  933   				.toStatementString();
  934   	}
  935   
  936   	protected SelectFragment generateSelectFragment(String alias, String columnSuffix) {
  937   		return new SelectFragment()
  938   				.setSuffix( columnSuffix )
  939   				.addColumns( alias, keyColumnNames, keyColumnAliases );
  940   	}
  941   
  942   	protected void appendElementColumns(SelectFragment frag, String elemAlias) {
  943   		for ( int i=0; i<elementColumnIsSettable.length; i++ ) {
  944   			if ( elementColumnIsSettable[i] ) {
  945   				frag.addColumn( elemAlias, elementColumnNames[i], elementColumnAliases[i] );
  946   			}
  947   			else {
  948   				frag.addFormula( elemAlias, elementFormulaTemplates[i], elementColumnAliases[i] );
  949   			}
  950   		}
  951   	}
  952   
  953   	protected void appendIndexColumns(SelectFragment frag, String alias) {
  954   		if ( hasIndex ) {
  955   			for ( int i=0; i<indexColumnIsSettable.length; i++ ) {
  956   				if ( indexColumnIsSettable[i] ) {
  957   					frag.addColumn( alias, indexColumnNames[i], indexColumnAliases[i] );
  958   				}
  959   				else {
  960   					frag.addFormula( alias, indexFormulaTemplates[i], indexColumnAliases[i] );
  961   				}
  962   			}
  963   		}
  964   	}
  965   
  966   	protected void appendIdentifierColumns(SelectFragment frag, String alias) {
  967   		if ( hasIdentifier ) {
  968   			frag.addColumn( alias, identifierColumnName, identifierColumnAlias );
  969   		}
  970   	}
  971   
  972   	public String[] getIndexColumnNames() {
  973   		return indexColumnNames;
  974   	}
  975   
  976   	public String[] getIndexFormulas() {
  977   		return indexFormulas;
  978   	}
  979   
  980   	public String[] getIndexColumnNames(String alias) {
  981   		return qualify(alias, indexColumnNames, indexFormulaTemplates);
  982   
  983   	}
  984   
  985   	public String[] getElementColumnNames(String alias) {
  986   		return qualify(alias, elementColumnNames, elementFormulaTemplates);
  987   	}
  988   	
  989   	private static String[] qualify(String alias, String[] columnNames, String[] formulaTemplates) {
  990   		int span = columnNames.length;
  991   		String[] result = new String[span];
  992   		for (int i=0; i<span; i++) {
  993   			if ( columnNames[i]==null ) {
  994   				result[i] = StringHelper.replace( formulaTemplates[i], Template.TEMPLATE, alias );
  995   			}
  996   			else {
  997   				result[i] = StringHelper.qualify( alias, columnNames[i] );
  998   			}
  999   		}
 1000   		return result;
 1001   	}
 1002   
 1003   	public String[] getElementColumnNames() {
 1004   		return elementColumnNames; //TODO: something with formulas...
 1005   	}
 1006   
 1007   	public String[] getKeyColumnNames() {
 1008   		return keyColumnNames;
 1009   	}
 1010   
 1011   	public boolean hasIndex() {
 1012   		return hasIndex;
 1013   	}
 1014   
 1015   	public boolean isLazy() {
 1016   		return isLazy;
 1017   	}
 1018   
 1019   	public boolean isInverse() {
 1020   		return isInverse;
 1021   	}
 1022   
 1023   	public String getTableName() {
 1024   		return qualifiedTableName;
 1025   	}
 1026   
 1027   	public void remove(Serializable id, SessionImplementor session) throws HibernateException {
 1028   
 1029   		if ( !isInverse && isRowDeleteEnabled() ) {
 1030   
 1031   			if ( log.isDebugEnabled() ) {
 1032   				log.debug( 
 1033   						"Deleting collection: " + 
 1034   						MessageHelper.collectionInfoString( this, id, getFactory() ) 
 1035   					);
 1036   			}
 1037   
 1038   			// Remove all the old entries
 1039   
 1040   			try {
 1041   				int offset = 1;
 1042   				PreparedStatement st = null;
 1043   				Expectation expectation = Expectations.appropriateExpectation( getDeleteAllCheckStyle() );
 1044   				boolean callable = isDeleteAllCallable();
 1045   				boolean useBatch = expectation.canBeBatched();
 1046   				String sql = getSQLDeleteString();
 1047   				if ( useBatch ) {
 1048   					if ( callable ) {
 1049   						st = session.getBatcher().prepareBatchCallableStatement( sql );
 1050   					}
 1051   					else {
 1052   						st = session.getBatcher().prepareBatchStatement( sql );
 1053   					}
 1054   				}
 1055   				else {
 1056   					if ( callable ) {
 1057   						st = session.getBatcher().prepareCallableStatement( sql );
 1058   					}
 1059   					else {
 1060   						st = session.getBatcher().prepareStatement( sql );
 1061   					}
 1062   				}
 1063   
 1064   
 1065   				try {
 1066   					offset+= expectation.prepare( st );
 1067   
 1068   					writeKey( st, id, offset, session );
 1069   					if ( useBatch ) {
 1070   						session.getBatcher().addToBatch( expectation );
 1071   					}
 1072   					else {
 1073   						expectation.verifyOutcome( st.executeUpdate(), st, -1 );
 1074   					}
 1075   				}
 1076   				catch ( SQLException sqle ) {
 1077   					if ( useBatch ) {
 1078   						session.getBatcher().abortBatch( sqle );
 1079   					}
 1080   					throw sqle;
 1081   				}
 1082   				finally {
 1083   					if ( !useBatch ) {
 1084   						session.getBatcher().closeStatement( st );
 1085   					}
 1086   				}
 1087   
 1088   				if ( log.isDebugEnabled() ) {
 1089   					log.debug( "done deleting collection" );
 1090   				}
 1091   			}
 1092   			catch ( SQLException sqle ) {
 1093   				throw JDBCExceptionHelper.convert(
 1094   				        sqlExceptionConverter,
 1095   				        sqle,
 1096   				        "could not delete collection: " + 
 1097   				        MessageHelper.collectionInfoString( this, id, getFactory() ),
 1098   				        getSQLDeleteString()
 1099   					);
 1100   			}
 1101   
 1102   		}
 1103   
 1104   	}
 1105   
 1106   	public void recreate(PersistentCollection collection, Serializable id, SessionImplementor session)
 1107   			throws HibernateException {
 1108   
 1109   		if ( !isInverse && isRowInsertEnabled() ) {
 1110   
 1111   			if ( log.isDebugEnabled() ) {
 1112   				log.debug( 
 1113   						"Inserting collection: " + 
 1114   						MessageHelper.collectionInfoString( this, id, getFactory() ) 
 1115   					);
 1116   			}
 1117   
 1118   			try {
 1119   				//create all the new entries
 1120   				Iterator entries = collection.entries(this);
 1121   				if ( entries.hasNext() ) {
 1122   					collection.preInsert( this );
 1123   					int i = 0;
 1124   					int count = 0;
 1125   					while ( entries.hasNext() ) {
 1126   
 1127   						final Object entry = entries.next();
 1128   						if ( collection.entryExists( entry, i ) ) {
 1129   							int offset = 1;
 1130   							PreparedStatement st = null;
 1131   							Expectation expectation = Expectations.appropriateExpectation( getInsertCheckStyle() );
 1132   							boolean callable = isInsertCallable();
 1133   							boolean useBatch = expectation.canBeBatched();
 1134   							String sql = getSQLInsertRowString();
 1135   
 1136   							if ( useBatch ) {
 1137   								if ( callable ) {
 1138   									st = session.getBatcher().prepareBatchCallableStatement( sql );
 1139   								}
 1140   								else {
 1141   									st = session.getBatcher().prepareBatchStatement( sql );
 1142   								}
 1143   							}
 1144   							else {
 1145   								if ( callable ) {
 1146   									st = session.getBatcher().prepareCallableStatement( sql );
 1147   								}
 1148   								else {
 1149   									st = session.getBatcher().prepareStatement( sql );
 1150   								}
 1151   							}
 1152   
 1153   
 1154   							try {
 1155   								offset+= expectation.prepare( st );
 1156   
 1157   								//TODO: copy/paste from insertRows()
 1158   								int loc = writeKey( st, id, offset, session );
 1159   								if ( hasIdentifier ) {
 1160   									loc = writeIdentifier( st, collection.getIdentifier(entry, i), loc, session );
 1161   								}
 1162   								if ( hasIndex /*&& !indexIsFormula*/ ) {
 1163   									loc = writeIndex( st, collection.getIndex(entry, i, this), loc, session );
 1164   								}
 1165   								loc = writeElement(st, collection.getElement(entry), loc, session );
 1166   
 1167   								if ( useBatch ) {
 1168   									session.getBatcher().addToBatch( expectation );
 1169   								}
 1170   								else {
 1171   									expectation.verifyOutcome( st.executeUpdate(), st, -1 );
 1172   								}
 1173   
 1174   								collection.afterRowInsert( this, entry, i );
 1175   								count++;
 1176   							}
 1177   							catch ( SQLException sqle ) {
 1178   								if ( useBatch ) {
 1179   									session.getBatcher().abortBatch( sqle );
 1180   								}
 1181   								throw sqle;
 1182   							}
 1183   							finally {
 1184   								if ( !useBatch ) {
 1185   									session.getBatcher().closeStatement( st );
 1186   								}
 1187   							}
 1188   
 1189   						}
 1190   						i++;
 1191   					}
 1192   
 1193   					if ( log.isDebugEnabled() ) {
 1194   						log.debug( "done inserting collection: " + count + " rows inserted" );
 1195   					}
 1196   
 1197   				}
 1198   				else {
 1199   					if ( log.isDebugEnabled() ) {
 1200   						log.debug( "collection was empty" );
 1201   					}
 1202   				}
 1203   			}
 1204   			catch ( SQLException sqle ) {
 1205   				throw JDBCExceptionHelper.convert(
 1206   				        sqlExceptionConverter,
 1207   				        sqle,
 1208   				        "could not insert collection: " + 
 1209   				        MessageHelper.collectionInfoString( this, id, getFactory() ),
 1210   				        getSQLInsertRowString()
 1211   					);
 1212   			}
 1213   		}
 1214   	}
 1215   	
 1216   	protected boolean isRowDeleteEnabled() {
 1217   		return true;
 1218   	}
 1219   
 1220   	public void deleteRows(PersistentCollection collection, Serializable id, SessionImplementor session)
 1221   			throws HibernateException {
 1222   
 1223   		if ( !isInverse && isRowDeleteEnabled() ) {
 1224   
 1225   			if ( log.isDebugEnabled() ) {
 1226   				log.debug( 
 1227   						"Deleting rows of collection: " + 
 1228   						MessageHelper.collectionInfoString( this, id, getFactory() ) 
 1229   					);
 1230   			}
 1231   			
 1232   			boolean deleteByIndex = !isOneToMany() && hasIndex && !indexContainsFormula;
 1233   			
 1234   			try {
 1235   				//delete all the deleted entries
 1236   				Iterator deletes = collection.getDeletes( this, !deleteByIndex );
 1237   				if ( deletes.hasNext() ) {
 1238   					int offset = 1;
 1239   					int count = 0;
 1240   					while ( deletes.hasNext() ) {
 1241   						PreparedStatement st = null;
 1242   						Expectation expectation = Expectations.appropriateExpectation( getDeleteCheckStyle() );
 1243   						boolean callable = isDeleteCallable();
 1244   						boolean useBatch = expectation.canBeBatched();
 1245   						String sql = getSQLDeleteRowString();
 1246   
 1247   						if ( useBatch ) {
 1248   							if ( callable ) {
 1249   								st = session.getBatcher().prepareBatchCallableStatement( sql );
 1250   							}
 1251   							else {
 1252   								st = session.getBatcher().prepareBatchStatement( sql );
 1253   							}
 1254   						}
 1255   						else {
 1256   							if ( callable ) {
 1257   								st = session.getBatcher().prepareCallableStatement( sql );
 1258   							}
 1259   							else {
 1260   								st = session.getBatcher().prepareStatement( sql );
 1261   							}
 1262   						}
 1263   
 1264   						try {
 1265   							expectation.prepare( st );
 1266   
 1267   							Object entry = deletes.next();
 1268   							int loc = offset;
 1269   							if ( hasIdentifier ) {
 1270   								writeIdentifier( st, entry, loc, session );
 1271   							}
 1272   							else {
 1273   								loc = writeKey( st, id, loc, session );
 1274   								if ( deleteByIndex ) {
 1275   									writeIndexToWhere( st, entry, loc, session );
 1276   								}
 1277   								else {
 1278   									writeElementToWhere( st, entry, loc, session );
 1279   								}
 1280   							}
 1281   
 1282   							if ( useBatch ) {
 1283   								session.getBatcher().addToBatch( expectation );
 1284   							}
 1285   							else {
 1286   								expectation.verifyOutcome( st.executeUpdate(), st, -1 );
 1287   							}
 1288   							count++;
 1289   						}
 1290   						catch ( SQLException sqle ) {
 1291   							if ( useBatch ) {
 1292   								session.getBatcher().abortBatch( sqle );
 1293   							}
 1294   							throw sqle;
 1295   						}
 1296   						finally {
 1297   							if ( !useBatch ) {
 1298   								session.getBatcher().closeStatement( st );
 1299   							}
 1300   						}
 1301   
 1302   						if ( log.isDebugEnabled() ) {
 1303   							log.debug( "done deleting collection rows: " + count + " deleted" );
 1304   						}
 1305   					}
 1306   				}
 1307   				else {
 1308   					if ( log.isDebugEnabled() ) {
 1309   						log.debug( "no rows to delete" );
 1310   					}
 1311   				}
 1312   			}
 1313   			catch ( SQLException sqle ) {
 1314   				throw JDBCExceptionHelper.convert(
 1315   				        sqlExceptionConverter,
 1316   				        sqle,
 1317   				        "could not delete collection rows: " + 
 1318   				        MessageHelper.collectionInfoString( this, id, getFactory() ),
 1319   				        getSQLDeleteRowString()
 1320   					);
 1321   			}
 1322   		}
 1323   	}
 1324   	
 1325   	protected boolean isRowInsertEnabled() {
 1326   		return true;
 1327   	}
 1328   
 1329   	public void insertRows(PersistentCollection collection, Serializable id, SessionImplementor session)
 1330   			throws HibernateException {
 1331   
 1332   		if ( !isInverse && isRowInsertEnabled() ) {
 1333   
 1334   			if ( log.isDebugEnabled() ) {
 1335   				log.debug( 
 1336   						"Inserting rows of collection: " + 
 1337   						MessageHelper.collectionInfoString( this, id, getFactory() ) 
 1338   					);
 1339   			}
 1340   
 1341   			try {
 1342   				//insert all the new entries
 1343   				collection.preInsert( this );
 1344   				Iterator entries = collection.entries( this );
 1345   				Expectation expectation = Expectations.appropriateExpectation( getInsertCheckStyle() );
 1346   				boolean callable = isInsertCallable();
 1347   				boolean useBatch = expectation.canBeBatched();
 1348   				String sql = getSQLInsertRowString();
 1349   				int i = 0;
 1350   				int count = 0;
 1351   				while ( entries.hasNext() ) {
 1352   					int offset = 1;
 1353   					Object entry = entries.next();
 1354   					PreparedStatement st = null;
 1355   					if ( collection.needsInserting( entry, i, elementType ) ) {
 1356   
 1357   						if ( useBatch ) {
 1358   							if ( st == null ) {
 1359   								if ( callable ) {
 1360   									st = session.getBatcher().prepareBatchCallableStatement( sql );
 1361   								}
 1362   								else {
 1363   									st = session.getBatcher().prepareBatchStatement( sql );
 1364   								}
 1365   							}
 1366   						}
 1367   						else {
 1368   							if ( callable ) {
 1369   								st = session.getBatcher().prepareCallableStatement( sql );
 1370   							}
 1371   							else {
 1372   								st = session.getBatcher().prepareStatement( sql );
 1373   							}
 1374   						}
 1375   
 1376   						try {
 1377   							offset += expectation.prepare( st );
 1378   							//TODO: copy/paste from recreate()
 1379   							offset = writeKey( st, id, offset, session );
 1380   							if ( hasIdentifier ) {
 1381   								offset = writeIdentifier( st, collection.getIdentifier(entry, i), offset, session );
 1382   							}
 1383   							if ( hasIndex /*&& !indexIsFormula*/ ) {
 1384   								offset = writeIndex( st, collection.getIndex(entry, i, this), offset, session );
 1385   							}
 1386   							writeElement(st, collection.getElement(entry), offset, session );
 1387   
 1388   							if ( useBatch ) {
 1389   								session.getBatcher().addToBatch( expectation );
 1390   							}
 1391   							else {
 1392   								expectation.verifyOutcome( st.executeUpdate(), st, -1 );
 1393   							}
 1394   							collection.afterRowInsert( this, entry, i );
 1395   							count++;
 1396   						}
 1397   						catch ( SQLException sqle ) {
 1398   							if ( useBatch ) {
 1399   								session.getBatcher().abortBatch( sqle );
 1400   							}
 1401   							throw sqle;
 1402   						}
 1403   						finally {
 1404   							if ( !useBatch ) {
 1405   								session.getBatcher().closeStatement( st );
 1406   							}
 1407   						}
 1408   					}
 1409   					i++;
 1410   				}
 1411   				if ( log.isDebugEnabled() ) {
 1412   					log.debug( "done inserting rows: " + count + " inserted" );
 1413   				}
 1414   			}
 1415   			catch ( SQLException sqle ) {
 1416   				throw JDBCExceptionHelper.convert(
 1417   				        sqlExceptionConverter,
 1418   				        sqle,
 1419   				        "could not insert collection rows: " + 
 1420   				        MessageHelper.collectionInfoString( this, id, getFactory() ),
 1421   				        getSQLInsertRowString()
 1422   					);
 1423   			}
 1424   
 1425   		}
 1426   	}
 1427   
 1428   
 1429   	public String getRole() {
 1430   		return role;
 1431   	}
 1432   
 1433   	public String getOwnerEntityName() {
 1434   		return entityName;
 1435   	}
 1436   
 1437   	public EntityPersister getOwnerEntityPersister() {
 1438   		return ownerPersister;
 1439   	}
 1440   
 1441   	public IdentifierGenerator getIdentifierGenerator() {
 1442   		return identifierGenerator;
 1443   	}
 1444   
 1445   	public Type getIdentifierType() {
 1446   		return identifierType;
 1447   	}
 1448   
 1449   	public boolean hasOrphanDelete() {
 1450   		return hasOrphanDelete;
 1451   	}
 1452   
 1453   	public Type toType(String propertyName) throws QueryException {
 1454   		if ( "index".equals( propertyName ) ) {
 1455   			return indexType;
 1456   		}
 1457   		return elementPropertyMapping.toType( propertyName );
 1458   	}
 1459   
 1460   	public abstract boolean isManyToMany();
 1461   
 1462   	public String getManyToManyFilterFragment(String alias, Map enabledFilters) {
 1463   		StringBuffer buffer = new StringBuffer();
 1464   		manyToManyFilterHelper.render( buffer, alias, enabledFilters );
 1465   
 1466   		if ( manyToManyWhereString != null ) {
 1467   			buffer.append( " and " )
 1468   					.append( StringHelper.replace( manyToManyWhereTemplate, Template.TEMPLATE, alias ) );
 1469   		}
 1470   
 1471   		return buffer.toString();
 1472   	}
 1473   
 1474   	public String[] toColumns(String alias, String propertyName)
 1475   			throws QueryException {
 1476   
 1477   		if ( "index".equals( propertyName ) ) {
 1478   			if ( isManyToMany() ) {
 1479   				throw new QueryException( "index() function not supported for many-to-many association" );
 1480   			}
 1481   			return StringHelper.qualify( alias, indexColumnNames );
 1482   		}
 1483   
 1484   		return elementPropertyMapping.toColumns( alias, propertyName );
 1485   	}
 1486   
 1487   	public String[] toColumns(String propertyName)
 1488   			throws QueryException {
 1489   
 1490   		if ( "index".equals( propertyName ) ) {
 1491   			if ( isManyToMany() ) {
 1492   				throw new QueryException( "index() function not supported for many-to-many association" );
 1493   			}
 1494   			return indexColumnNames;
 1495   		}
 1496   
 1497   		return elementPropertyMapping.toColumns( propertyName );
 1498   	}
 1499   
 1500   	public Type getType() {
 1501   		return elementPropertyMapping.getType(); //==elementType ??
 1502   	}
 1503   
 1504   	public String getName() {
 1505   		return getRole();
 1506   	}
 1507   
 1508   	public EntityPersister getElementPersister() {
 1509   		if ( elementPersister == null ) {
 1510   			throw new AssertionFailure( "not an association" );
 1511   		}
 1512   		return ( Loadable ) elementPersister;
 1513   	}
 1514   
 1515   	public boolean isCollection() {
 1516   		return true;
 1517   	}
 1518   
 1519   	public Serializable[] getCollectionSpaces() {
 1520   		return spaces;
 1521   	}
 1522   
 1523   	protected abstract String generateDeleteString();
 1524   
 1525   	protected abstract String generateDeleteRowString();
 1526   
 1527   	protected abstract String generateUpdateRowString();
 1528   
 1529   	protected abstract String generateInsertRowString();
 1530   
 1531   	public void updateRows(PersistentCollection collection, Serializable id, SessionImplementor session) 
 1532   	throws HibernateException {
 1533   
 1534   		if ( !isInverse && collection.isRowUpdatePossible() ) {
 1535   
 1536   			if ( log.isDebugEnabled() ) {
 1537   				log.debug( "Updating rows of collection: " + role + "#" + id );
 1538   			}
 1539   
 1540   			//update all the modified entries
 1541   			int count = doUpdateRows( id, collection, session );
 1542   
 1543   			if ( log.isDebugEnabled() ) {
 1544   				log.debug( "done updating rows: " + count + " updated" );
 1545   			}
 1546   		}
 1547   	}
 1548   
 1549   	protected abstract int doUpdateRows(Serializable key, PersistentCollection collection, SessionImplementor session) 
 1550   	throws HibernateException;
 1551   
 1552   	public CollectionMetadata getCollectionMetadata() {
 1553   		return this;
 1554   	}
 1555   
 1556   	public SessionFactoryImplementor getFactory() {
 1557   		return factory;
 1558   	}
 1559   
 1560   	protected String filterFragment(String alias) throws MappingException {
 1561   		return hasWhere() ? " and " + getSQLWhereString( alias ) : "";
 1562   	}
 1563   
 1564   	public String filterFragment(String alias, Map enabledFilters) throws MappingException {
 1565   
 1566   		StringBuffer sessionFilterFragment = new StringBuffer();
 1567   		filterHelper.render( sessionFilterFragment, alias, enabledFilters );
 1568   
 1569   		return sessionFilterFragment.append( filterFragment( alias ) ).toString();
 1570   	}
 1571   
 1572   	public String oneToManyFilterFragment(String alias) throws MappingException {
 1573   		return "";
 1574   	}
 1575   
 1576   	protected boolean isInsertCallable() {
 1577   		return insertCallable;
 1578   	}
 1579   
 1580   	protected ExecuteUpdateResultCheckStyle getInsertCheckStyle() {
 1581   		return insertCheckStyle;
 1582   	}
 1583   
 1584   	protected boolean isUpdateCallable() {
 1585   		return updateCallable;
 1586   	}
 1587   
 1588   	protected ExecuteUpdateResultCheckStyle getUpdateCheckStyle() {
 1589   		return updateCheckStyle;
 1590   	}
 1591   
 1592   	protected boolean isDeleteCallable() {
 1593   		return deleteCallable;
 1594   	}
 1595   
 1596   	protected ExecuteUpdateResultCheckStyle getDeleteCheckStyle() {
 1597   		return deleteCheckStyle;
 1598   	}
 1599   
 1600   	protected boolean isDeleteAllCallable() {
 1601   		return deleteAllCallable;
 1602   	}
 1603   
 1604   	protected ExecuteUpdateResultCheckStyle getDeleteAllCheckStyle() {
 1605   		return deleteAllCheckStyle;
 1606   	}
 1607   
 1608   	public String toString() {
 1609   		return StringHelper.unqualify( getClass().getName() ) + '(' + role + ')';
 1610   	}
 1611   
 1612   	public boolean isVersioned() {
 1613   		return isVersioned && getOwnerEntityPersister().isVersioned();
 1614   	}
 1615   	
 1616   	public String getNodeName() {
 1617   		return nodeName;
 1618   	}
 1619   
 1620   	public String getElementNodeName() {
 1621   		return elementNodeName;
 1622   	}
 1623   
 1624   	public String getIndexNodeName() {
 1625   		return indexNodeName;
 1626   	}
 1627   
 1628   	protected SQLExceptionConverter getSQLExceptionConverter() {
 1629   		return sqlExceptionConverter;
 1630   	}
 1631   
 1632   	public CacheEntryStructure getCacheEntryStructure() {
 1633   		return cacheEntryStructure;
 1634   	}
 1635   
 1636   	public boolean isAffectedByEnabledFilters(SessionImplementor session) {
 1637   		return filterHelper.isAffectedBy( session.getEnabledFilters() ) ||
 1638   		        ( isManyToMany() && manyToManyFilterHelper.isAffectedBy( session.getEnabledFilters() ) );
 1639   	}
 1640   
 1641   	public boolean isSubselectLoadable() {
 1642   		return subselectLoadable;
 1643   	}
 1644   	
 1645   	public boolean isMutable() {
 1646   		return isMutable;
 1647   	}
 1648   
 1649   	public String[] getCollectionPropertyColumnAliases(String propertyName, String suffix) {
 1650   		String rawAliases[] = (String[]) collectionPropertyColumnAliases.get(propertyName);
 1651   
 1652   		if ( rawAliases == null ) {
 1653   			return null;
 1654   		}
 1655   		
 1656   		String result[] = new String[rawAliases.length];
 1657   		for ( int i=0; i<rawAliases.length; i++ ) {
 1658   			result[i] = new Alias(suffix).toUnquotedAliasString( rawAliases[i] );
 1659   		}
 1660   		return result;
 1661   	}
 1662   	
 1663   	//TODO: formulas ?
 1664   	public void initCollectionPropertyMap() {
 1665   
 1666   		initCollectionPropertyMap( "key", keyType, keyColumnAliases, keyColumnNames );
 1667   		initCollectionPropertyMap( "element", elementType, elementColumnAliases, elementColumnNames );
 1668   		if (hasIndex) {
 1669   			initCollectionPropertyMap( "index", indexType, indexColumnAliases, indexColumnNames );
 1670   		}
 1671   		if (hasIdentifier) {
 1672   			initCollectionPropertyMap( 
 1673   					"id", 
 1674   					identifierType, 
 1675   					new String[] { identifierColumnAlias }, 
 1676   					new String[] { identifierColumnName } 
 1677   				);
 1678   		}
 1679   	}
 1680   
 1681   	private void initCollectionPropertyMap(String aliasName, Type type, String[] columnAliases, String[] columnNames) {
 1682   		
 1683   		collectionPropertyColumnAliases.put(aliasName, columnAliases);
 1684   		collectionPropertyColumnNames.put(aliasName, columnNames);
 1685   	
 1686   		if( type.isComponentType() ) {
 1687   			AbstractComponentType ct = (AbstractComponentType) type;
 1688   			String[] propertyNames = ct.getPropertyNames();
 1689   			for (int i = 0; i < propertyNames.length; i++) {
 1690   				String name = propertyNames[i];
 1691   				collectionPropertyColumnAliases.put( aliasName + "." + name, columnAliases[i] );
 1692   				collectionPropertyColumnNames.put( aliasName + "." + name, columnNames[i] );
 1693   			}
 1694   		} 
 1695   		
 1696   	}
 1697   
 1698   	public int getSize(Serializable key, SessionImplementor session) {
 1699   		try {
 1700   			PreparedStatement st = session.getBatcher().prepareSelectStatement(sqlSelectSizeString);
 1701   			try {
 1702   				getKeyType().nullSafeSet(st, key, 1, session);
 1703   				ResultSet rs = st.executeQuery();
 1704   				try {
 1705   					return rs.next() ? rs.getInt(1) - baseIndex : 0;
 1706   				}
 1707   				finally {
 1708   					rs.close();
 1709   				}
 1710   			}
 1711   			finally {
 1712   				session.getBatcher().closeStatement( st );
 1713   			}
 1714   		}
 1715   		catch (SQLException sqle) {
 1716   			throw JDBCExceptionHelper.convert(
 1717   					getFactory().getSQLExceptionConverter(),
 1718   					sqle,
 1719   					"could not retrieve collection size: " + 
 1720   					MessageHelper.collectionInfoString( this, key, getFactory() ),
 1721   					sqlSelectSizeString
 1722   				);
 1723   		}
 1724   	}
 1725   	
 1726   	public boolean indexExists(Serializable key, Object index, SessionImplementor session) {
 1727   		return exists(key, incrementIndexByBase(index), getIndexType(), sqlDetectRowByIndexString, session);
 1728   	}
 1729   
 1730   	public boolean elementExists(Serializable key, Object element, SessionImplementor session) {
 1731   		return exists(key, element, getElementType(), sqlDetectRowByElementString, session);
 1732   	}
 1733   
 1734   	private boolean exists(Serializable key, Object indexOrElement, Type indexOrElementType, String sql, SessionImplementor session) {
 1735   		try {
 1736   			PreparedStatement st = session.getBatcher().prepareSelectStatement(sql);
 1737   			try {
 1738   				getKeyType().nullSafeSet(st, key, 1, session);
 1739   				indexOrElementType.nullSafeSet( st, indexOrElement, keyColumnNames.length + 1, session );
 1740   				ResultSet rs = st.executeQuery();
 1741   				try {
 1742   					return rs.next();
 1743   				}
 1744   				finally {
 1745   					rs.close();
 1746   				}
 1747   			}
 1748   			catch( TransientObjectException e ) {
 1749   				return false;
 1750   			}
 1751   			finally {
 1752   				session.getBatcher().closeStatement( st );
 1753   			}
 1754   		}
 1755   		catch (SQLException sqle) {
 1756   			throw JDBCExceptionHelper.convert(
 1757   					getFactory().getSQLExceptionConverter(),
 1758   					sqle,
 1759   					"could not check row existence: " + 
 1760   					MessageHelper.collectionInfoString( this, key, getFactory() ),
 1761   					sqlSelectSizeString
 1762   				);
 1763   		}
 1764   	}
 1765   
 1766   	public Object getElementByIndex(Serializable key, Object index, SessionImplementor session, Object owner) {
 1767   		try {
 1768   			PreparedStatement st = session.getBatcher().prepareSelectStatement(sqlSelectRowByIndexString);
 1769   			try {
 1770   				getKeyType().nullSafeSet(st, key, 1, session);
 1771   				getIndexType().nullSafeSet( st, incrementIndexByBase(index), keyColumnNames.length + 1, session );
 1772   				ResultSet rs = st.executeQuery();
 1773   				try {
 1774   					if ( rs.next() ) {
 1775   						return getElementType().nullSafeGet(rs, elementColumnAliases, session, owner);
 1776   					}
 1777   					else {
 1778   						return null;
 1779   					}
 1780   				}
 1781   				finally {
 1782   					rs.close();
 1783   				}
 1784   			}
 1785   			finally {
 1786   				session.getBatcher().closeStatement( st );
 1787   			}
 1788   		}
 1789   		catch (SQLException sqle) {
 1790   			throw JDBCExceptionHelper.convert(
 1791   					getFactory().getSQLExceptionConverter(),
 1792   					sqle,
 1793   					"could not read row: " + 
 1794   					MessageHelper.collectionInfoString( this, key, getFactory() ),
 1795   					sqlSelectSizeString
 1796   				);
 1797   		}
 1798   	}
 1799   
 1800   	public boolean isExtraLazy() {
 1801   		return isExtraLazy;
 1802   	}
 1803   	
 1804   	protected Dialect getDialect() {
 1805   		return dialect;
 1806   	}
 1807   }

Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » persister » collection » [javadoc | source]