package org.hibernate.persister.collection;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.hibernate.AssertionFailure;
import org.hibernate.FetchMode;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.TransientObjectException;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.access.CollectionDataAccess;
import org.hibernate.cache.spi.entry.CacheEntryStructure;
import org.hibernate.cache.spi.entry.StructuredCollectionCacheEntry;
import org.hibernate.cache.spi.entry.StructuredMapCacheEntry;
import org.hibernate.cache.spi.entry.UnstructuredCacheEntry;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.batch.internal.BasicBatchKey;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.SubselectFetch;
import org.hibernate.exception.spi.SQLExceptionConverter;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.FilterAliasGenerator;
import org.hibernate.internal.FilterHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.jdbc.Expectation;
import org.hibernate.jdbc.Expectations;
import org.hibernate.loader.collection.CollectionInitializer;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Formula;
import org.hibernate.mapping.IdentifierCollection;
import org.hibernate.mapping.IndexedCollection;
import org.hibernate.mapping.List;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Table;
import org.hibernate.metadata.CollectionMetadata;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.entity.PropertyMapping;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.spi.PersisterCreationContext;
import org.hibernate.persister.walking.internal.CompositionSingularSubAttributesHelper;
import org.hibernate.persister.walking.internal.StandardAnyTypeDefinition;
import org.hibernate.persister.walking.spi.AnyMappingDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeSource;
import org.hibernate.persister.walking.spi.CollectionDefinition;
import org.hibernate.persister.walking.spi.CollectionElementDefinition;
import org.hibernate.persister.walking.spi.CollectionIndexDefinition;
import org.hibernate.persister.walking.spi.CompositeCollectionElementDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.persister.walking.spi.EntityDefinition;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.sql.Alias;
import org.hibernate.sql.SelectFragment;
import org.hibernate.sql.SimpleSelect;
import org.hibernate.sql.Template;
import org.hibernate.sql.ordering.antlr.ColumnMapper;
import org.hibernate.sql.ordering.antlr.ColumnReference;
import org.hibernate.sql.ordering.antlr.FormulaReference;
import org.hibernate.sql.ordering.antlr.OrderByAliasResolver;
import org.hibernate.sql.ordering.antlr.OrderByTranslation;
import org.hibernate.sql.ordering.antlr.SqlValueReference;
import org.hibernate.type.AnyType;
import org.hibernate.type.AssociationType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.jboss.logging.Logger;
public abstract class AbstractCollectionPersister
implements CollectionMetadata, SQLLoadableCollection {
private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class,
AbstractCollectionPersister.class.getName() );
private final NavigableRole navigableRole;
private final String sqlDeleteString;
private final String sqlInsertRowString;
private final String sqlUpdateRowString;
private final String sqlDeleteRowString;
private final String sqlSelectSizeString;
private final String sqlSelectRowByIndexString;
private final String sqlDetectRowByIndexString;
private final String sqlDetectRowByElementString;
protected final boolean hasWhere;
protected final String sqlWhereString;
private final String sqlWhereStringTemplate;
private final boolean hasOrder;
private final OrderByTranslation orderByTranslation;
private final boolean hasManyToManyOrder;
private final OrderByTranslation manyToManyOrderByTranslation;
private final int baseIndex;
private String mappedByProperty;
protected final boolean indexContainsFormula;
protected final boolean elementIsPureFormula;
private final Type keyType;
private final Type indexType;
protected final Type elementType;
private final Type identifierType;
protected final String[] keyColumnNames;
protected final String[] indexColumnNames;
protected final String[] indexFormulaTemplates;
protected final String[] indexFormulas;
protected final boolean[] indexColumnIsGettable;
protected final boolean[] indexColumnIsSettable;
protected final String[] elementColumnNames;
protected final String[] elementColumnWriters;
protected final String[] elementColumnReaders;
protected final String[] elementColumnReaderTemplates;
protected final String[] elementFormulaTemplates;
protected final String[] elementFormulas;
protected final boolean[] elementColumnIsSettable;
protected final boolean[] elementColumnIsInPrimaryKey;
protected final String[] indexColumnAliases;
protected final String[] elementColumnAliases;
protected final String[] keyColumnAliases;
protected final String identifierColumnName;
private final String identifierColumnAlias;
protected final String qualifiedTableName;
private final String queryLoaderName;
private final boolean isPrimitiveArray;
private final boolean isArray;
protected final boolean hasIndex;
protected final boolean hasIdentifier;
private final boolean isLazy;
private final boolean ;
protected final boolean isInverse;
private final boolean isMutable;
private final boolean isVersioned;
protected final int batchSize;
private final FetchMode fetchMode;
private final boolean hasOrphanDelete;
private final boolean subselectLoadable;
private final Class elementClass;
private final String entityName;
private final Dialect dialect;
protected final SqlExceptionHelper sqlExceptionHelper;
private final SessionFactoryImplementor factory;
private final EntityPersister ownerPersister;
private final IdentifierGenerator identifierGenerator;
private final PropertyMapping elementPropertyMapping;
private final EntityPersister elementPersister;
private final CollectionDataAccess cacheAccessStrategy;
private final CollectionType collectionType;
private CollectionInitializer initializer;
private final CacheEntryStructure cacheEntryStructure;
private final FilterHelper filterHelper;
private final FilterHelper manyToManyFilterHelper;
private final String manyToManyWhereString;
private final String manyToManyWhereTemplate;
private final boolean insertCallable;
private final boolean updateCallable;
private final boolean deleteCallable;
private final boolean deleteAllCallable;
private ExecuteUpdateResultCheckStyle insertCheckStyle;
private ExecuteUpdateResultCheckStyle updateCheckStyle;
private ExecuteUpdateResultCheckStyle deleteCheckStyle;
private ExecuteUpdateResultCheckStyle deleteAllCheckStyle;
private final Serializable[] spaces;
private Map collectionPropertyColumnAliases = new HashMap();
private Map collectionPropertyColumnNames = new HashMap();
public AbstractCollectionPersister(
Collection collectionBinding,
CollectionDataAccess cacheAccessStrategy,
PersisterCreationContext creationContext) throws MappingException, CacheException {
final Database database = creationContext.getMetadata().getDatabase();
final JdbcEnvironment jdbcEnvironment = database.getJdbcEnvironment();
this.factory = creationContext.getSessionFactory();
this.cacheAccessStrategy = cacheAccessStrategy;
if ( factory.getSessionFactoryOptions().isStructuredCacheEntriesEnabled() ) {
cacheEntryStructure = collectionBinding.isMap()
? StructuredMapCacheEntry.INSTANCE
: StructuredCollectionCacheEntry.INSTANCE;
}
else {
cacheEntryStructure = UnstructuredCacheEntry.INSTANCE;
}
dialect = factory.getDialect();
sqlExceptionHelper = factory.getSQLExceptionHelper();
collectionType = collectionBinding.getCollectionType();
navigableRole = new NavigableRole( collectionBinding.getRole() );
entityName = collectionBinding.getOwnerEntityName();
ownerPersister = factory.getEntityPersister( entityName );
queryLoaderName = collectionBinding.getLoaderName();
isMutable = collectionBinding.isMutable();
mappedByProperty = collectionBinding.getMappedByProperty();
Table table = collectionBinding.getCollectionTable();
fetchMode = collectionBinding.getElement().getFetchMode();
elementType = collectionBinding.getElement().getType();
isPrimitiveArray = collectionBinding.isPrimitiveArray();
isArray = collectionBinding.isArray();
subselectLoadable = collectionBinding.isSubselectLoadable();
qualifiedTableName = determineTableName( table, jdbcEnvironment );
int spacesSize = 1 + collectionBinding.getSynchronizedTables().size();
spaces = new String[spacesSize];
spaces[0] = qualifiedTableName;
Iterator iter = collectionBinding.getSynchronizedTables().iterator();
for ( int i = 1; i < spacesSize; i++ ) {
spaces[i] = (String) iter.next();
}
sqlWhereString = StringHelper.isNotEmpty( collectionBinding.getWhere() ) ? "( " + collectionBinding.getWhere() + ") " : null;
hasWhere = sqlWhereString != null;
sqlWhereStringTemplate = hasWhere ?
Template.renderWhereStringTemplate( sqlWhereString, dialect, factory.getSqlFunctionRegistry() ) :
null;
hasOrphanDelete = collectionBinding.hasOrphanDelete();
int batch = collectionBinding.getBatchSize();
if ( batch == -1 ) {
batch = factory.getSessionFactoryOptions().getDefaultBatchFetchSize();
}
batchSize = batch;
isVersioned = collectionBinding.isOptimisticLocked();
keyType = collectionBinding.getKey().getType();
iter = collectionBinding.getKey().getColumnIterator();
int keySpan = collectionBinding.getKey().getColumnSpan();
keyColumnNames = new String[keySpan];
keyColumnAliases = new String[keySpan];
int k = 0;
while ( iter.hasNext() ) {
Column col = ( (Column) iter.next() );
keyColumnNames[k] = col.getQuotedName( dialect );
keyColumnAliases[k] = col.getAlias( dialect, table );
k++;
}
if ( elementType.isEntityType() ) {
String entityName = ( (EntityType) elementType ).getAssociatedEntityName();
elementPersister = factory.getEntityPersister( entityName );
}
else {
elementPersister = null;
}
int elementSpan = collectionBinding.getElement().getColumnSpan();
elementColumnAliases = new String[elementSpan];
elementColumnNames = new String[elementSpan];
elementColumnWriters = new String[elementSpan];
elementColumnReaders = new String[elementSpan];
elementColumnReaderTemplates = new String[elementSpan];
elementFormulaTemplates = new String[elementSpan];
elementFormulas = new String[elementSpan];
elementColumnIsSettable = new boolean[elementSpan];
elementColumnIsInPrimaryKey = new boolean[elementSpan];
boolean isPureFormula = true;
boolean hasNotNullableColumns = false;
int j = 0;
iter = collectionBinding.getElement().getColumnIterator();
while ( iter.hasNext() ) {
Selectable selectable = (Selectable) iter.next();
elementColumnAliases[j] = selectable.getAlias( dialect, table );
if ( selectable.isFormula() ) {
Formula form = (Formula) selectable;
elementFormulaTemplates[j] = form.getTemplate( dialect, factory.getSqlFunctionRegistry() );
elementFormulas[j] = form.getFormula();
}
else {
Column col = (Column) selectable;
elementColumnNames[j] = col.getQuotedName( dialect );
elementColumnWriters[j] = col.getWriteExpr();
elementColumnReaders[j] = col.getReadExpr( dialect );
elementColumnReaderTemplates[j] = col.getTemplate( dialect, factory.getSqlFunctionRegistry() );
elementColumnIsSettable[j] = true;
elementColumnIsInPrimaryKey[j] = !col.isNullable();
if ( !col.isNullable() ) {
hasNotNullableColumns = true;
}
isPureFormula = false;
}
j++;
}
elementIsPureFormula = isPureFormula;
if ( !hasNotNullableColumns ) {
Arrays.fill( elementColumnIsInPrimaryKey, true );
}
hasIndex = collectionBinding.isIndexed();
if ( hasIndex ) {
IndexedCollection indexedCollection = (IndexedCollection) collectionBinding;
indexType = indexedCollection.getIndex().getType();
int indexSpan = indexedCollection.getIndex().getColumnSpan();
boolean[] indexColumnInsertability = indexedCollection.getIndex().getColumnInsertability();
boolean[] indexColumnUpdatability = indexedCollection.getIndex().getColumnUpdateability();
iter = indexedCollection.getIndex().getColumnIterator();
indexColumnNames = new String[indexSpan];
indexFormulaTemplates = new String[indexSpan];
indexFormulas = new String[indexSpan];
indexColumnIsGettable = new boolean[indexSpan];
indexColumnIsSettable = new boolean[indexSpan];
indexColumnAliases = new String[indexSpan];
int i = 0;
boolean hasFormula = false;
while ( iter.hasNext() ) {
Selectable s = (Selectable) iter.next();
indexColumnAliases[i] = s.getAlias( dialect );
if ( s.isFormula() ) {
Formula indexForm = (Formula) s;
indexFormulaTemplates[i] = indexForm.getTemplate( dialect, factory.getSqlFunctionRegistry() );
indexFormulas[i] = indexForm.getFormula();
hasFormula = true;
}
else {
Column indexCol = (Column) s;
indexColumnNames[i] = indexCol.getQuotedName( dialect );
indexColumnIsGettable[i] = true;
indexColumnIsSettable[i] = indexColumnInsertability[i] || indexColumnUpdatability[i];
}
i++;
}
indexContainsFormula = hasFormula;
baseIndex = indexedCollection.isList() ?
( (List) indexedCollection ).getBaseIndex() : 0;
}
else {
indexContainsFormula = false;
indexColumnIsGettable = null;
indexColumnIsSettable = null;
indexFormulaTemplates = null;
indexFormulas = null;
indexType = null;
indexColumnNames = null;
indexColumnAliases = null;
baseIndex = 0;
}
hasIdentifier = collectionBinding.isIdentified();
if ( hasIdentifier ) {
if ( collectionBinding.isOneToMany() ) {
throw new MappingException( "one-to-many collections with identifiers are not supported" );
}
IdentifierCollection idColl = (IdentifierCollection) collectionBinding;
identifierType = idColl.getIdentifier().getType();
iter = idColl.getIdentifier().getColumnIterator();
Column col = (Column) iter.next();
identifierColumnName = col.getQuotedName( dialect );
identifierColumnAlias = col.getAlias( dialect );
identifierGenerator = idColl.getIdentifier().createIdentifierGenerator(
creationContext.getMetadata().getIdentifierGeneratorFactory(),
factory.getDialect(),
factory.getSettings().getDefaultCatalogName(),
factory.getSettings().getDefaultSchemaName(),
null
);
}
else {
identifierType = null;
identifierColumnName = null;
identifierColumnAlias = null;
identifierGenerator = null;
}
if ( collectionBinding.getCustomSQLInsert() == null ) {
sqlInsertRowString = generateInsertRowString();
insertCallable = false;
insertCheckStyle = ExecuteUpdateResultCheckStyle.COUNT;
}
else {
sqlInsertRowString = collectionBinding.getCustomSQLInsert();
insertCallable = collectionBinding.isCustomInsertCallable();
insertCheckStyle = collectionBinding.getCustomSQLInsertCheckStyle() == null
? ExecuteUpdateResultCheckStyle.determineDefault( collectionBinding.getCustomSQLInsert(), insertCallable )
: collectionBinding.getCustomSQLInsertCheckStyle();
}
if ( collectionBinding.getCustomSQLUpdate() == null ) {
sqlUpdateRowString = generateUpdateRowString();
updateCallable = false;
updateCheckStyle = ExecuteUpdateResultCheckStyle.COUNT;
}
else {
sqlUpdateRowString = collectionBinding.getCustomSQLUpdate();
updateCallable = collectionBinding.isCustomUpdateCallable();
updateCheckStyle = collectionBinding.getCustomSQLUpdateCheckStyle() == null
? ExecuteUpdateResultCheckStyle.determineDefault( collectionBinding.getCustomSQLUpdate(), insertCallable )
: collectionBinding.getCustomSQLUpdateCheckStyle();
}
if ( collectionBinding.getCustomSQLDelete() == null ) {
sqlDeleteRowString = generateDeleteRowString();
deleteCallable = false;
deleteCheckStyle = ExecuteUpdateResultCheckStyle.NONE;
}
else {
sqlDeleteRowString = collectionBinding.getCustomSQLDelete();
deleteCallable = collectionBinding.isCustomDeleteCallable();
deleteCheckStyle = ExecuteUpdateResultCheckStyle.NONE;
}
if ( collectionBinding.getCustomSQLDeleteAll() == null ) {
sqlDeleteString = generateDeleteString();
deleteAllCallable = false;
deleteAllCheckStyle = ExecuteUpdateResultCheckStyle.NONE;
}
else {
sqlDeleteString = collectionBinding.getCustomSQLDeleteAll();
deleteAllCallable = collectionBinding.isCustomDeleteAllCallable();
deleteAllCheckStyle = ExecuteUpdateResultCheckStyle.NONE;
}
sqlSelectSizeString = generateSelectSizeString( collectionBinding.isIndexed() && !collectionBinding.isMap() );
sqlDetectRowByIndexString = generateDetectRowByIndexString();
sqlDetectRowByElementString = generateDetectRowByElementString();
sqlSelectRowByIndexString = generateSelectRowByIndexString();
logStaticSQL();
isLazy = collectionBinding.isLazy();
isExtraLazy = collectionBinding.isExtraLazy();
isInverse = collectionBinding.isInverse();
if ( collectionBinding.isArray() ) {
elementClass = ( (org.hibernate.mapping.Array) collectionBinding ).getElementClass();
}
else {
elementClass = null;
}
if ( elementType.isComponentType() ) {
elementPropertyMapping = new CompositeElementPropertyMapping(
elementColumnNames,
elementColumnReaders,
elementColumnReaderTemplates,
elementFormulaTemplates,
(CompositeType) elementType,
factory
);
}
else if ( !elementType.isEntityType() ) {
elementPropertyMapping = new ElementPropertyMapping(
elementColumnNames,
elementType
);
}
else {
if ( elementPersister instanceof PropertyMapping ) {
elementPropertyMapping = (PropertyMapping) elementPersister;
}
else {
elementPropertyMapping = new ElementPropertyMapping(
elementColumnNames,
elementType
);
}
}
hasOrder = collectionBinding.getOrderBy() != null;
if ( hasOrder ) {
LOG.debugf( "Translating order-by fragment [%s] for collection role : %s", collectionBinding.getOrderBy(), getRole() );
orderByTranslation = Template.translateOrderBy(
collectionBinding.getOrderBy(),
new ColumnMapperImpl(),
factory,
dialect,
factory.getSqlFunctionRegistry()
);
}
else {
orderByTranslation = null;
}
filterHelper = new FilterHelper( collectionBinding.getFilters(), factory);
manyToManyFilterHelper = new FilterHelper( collectionBinding.getManyToManyFilters(), factory);
manyToManyWhereString = StringHelper.isNotEmpty( collectionBinding.getManyToManyWhere() ) ?
"( " + collectionBinding.getManyToManyWhere() + ")" :
null;
manyToManyWhereTemplate = manyToManyWhereString == null ?
null :
Template.renderWhereStringTemplate( manyToManyWhereString, factory.getDialect(), factory.getSqlFunctionRegistry() );
hasManyToManyOrder = collectionBinding.getManyToManyOrdering() != null;
if ( hasManyToManyOrder ) {
LOG.debugf( "Translating many-to-many order-by fragment [%s] for collection role : %s", collectionBinding.getOrderBy(), getRole() );
manyToManyOrderByTranslation = Template.translateOrderBy(
collectionBinding.getManyToManyOrdering(),
new ColumnMapperImpl(),
factory,
dialect,
factory.getSqlFunctionRegistry()
);
}
else {
manyToManyOrderByTranslation = null;
}
initCollectionPropertyMap();
}
protected String determineTableName(Table table, JdbcEnvironment jdbcEnvironment) {
if ( table.getSubselect() != null ) {
return "( " + table.getSubselect() + " )";
}
return jdbcEnvironment.getQualifiedObjectNameFormatter().format(
table.getQualifiedTableName(),
jdbcEnvironment.getDialect()
);
}
private class ColumnMapperImpl implements ColumnMapper {
@Override
public SqlValueReference[] map(String reference) {
final String[] columnNames;
final String[] formulaTemplates;
if ( "$element$".equals( reference ) ) {
columnNames = elementColumnNames;
formulaTemplates = elementFormulaTemplates;
}
else {
columnNames = elementPropertyMapping.toColumns( reference );
formulaTemplates = formulaTemplates( reference, columnNames.length );
}
final SqlValueReference[] result = new SqlValueReference[ columnNames.length ];
int i = 0;
for ( final String columnName : columnNames ) {
if ( columnName == null ) {
final String formulaTemplate = formulaTemplates[i];
result[i] = new FormulaReference() {
@Override
public String getFormulaFragment() {
return formulaTemplate;
}
};
}
else {
result[i] = new ColumnReference() {
@Override
public String getColumnName() {
return columnName;
}
};
}
i++;
}
return result;
}
}
private String[] formulaTemplates(String reference, int expectedSize) {
try {
final int propertyIndex = elementPersister.getEntityMetamodel().getPropertyIndex( reference );
return ( (Queryable) elementPersister ).getSubclassPropertyFormulaTemplateClosure()[propertyIndex];
}
catch (Exception e) {
return new String[expectedSize];
}
}
@Override
public void postInstantiate() throws MappingException {
initializer = queryLoaderName == null ?
createCollectionInitializer( LoadQueryInfluencers.NONE ) :
new NamedQueryCollectionInitializer( queryLoaderName, this );
}
protected void logStaticSQL() {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Static SQL for collection: %s", getRole() );
if ( getSQLInsertRowString() != null ) {
LOG.debugf( " Row insert: %s", getSQLInsertRowString() );
}
if ( getSQLUpdateRowString() != null ) {
LOG.debugf( " Row update: %s", getSQLUpdateRowString() );
}
if ( getSQLDeleteRowString() != null ) {
LOG.debugf( " Row delete: %s", getSQLDeleteRowString() );
}
if ( getSQLDeleteString() != null ) {
LOG.debugf( " One-shot delete: %s", getSQLDeleteString() );
}
}
}
@Override
public void initialize(Serializable key, SharedSessionContractImplementor session) throws HibernateException {
getAppropriateInitializer( key, session ).initialize( key, session );
}
protected CollectionInitializer getAppropriateInitializer(Serializable key, SharedSessionContractImplementor session) {
if ( queryLoaderName != null ) {
return initializer;
}
CollectionInitializer subselectInitializer = getSubselectInitializer( key, session );
if ( subselectInitializer != null ) {
return subselectInitializer;
}
else if ( session.getLoadQueryInfluencers().getEnabledFilters().isEmpty() ) {
return initializer;
}
else {
return createCollectionInitializer( session.getLoadQueryInfluencers() );
}
}
private CollectionInitializer getSubselectInitializer(Serializable key, SharedSessionContractImplementor session) {
if ( !isSubselectLoadable() ) {
return null;
}
final PersistenceContext persistenceContext = session.getPersistenceContext();
SubselectFetch subselect = persistenceContext.getBatchFetchQueue()
.getSubselect( session.generateEntityKey( key, getOwnerEntityPersister() ) );
if ( subselect == null ) {
return null;
}
else {
Iterator iter = subselect.getResult().iterator();
while ( iter.hasNext() ) {
if ( !persistenceContext.containsEntity( (EntityKey) iter.next() ) ) {
iter.remove();
}
}
return createSubselectInitializer( subselect, session );
}
}
protected abstract CollectionInitializer createSubselectInitializer(SubselectFetch subselect, SharedSessionContractImplementor session);
protected abstract CollectionInitializer createCollectionInitializer(LoadQueryInfluencers loadQueryInfluencers)
throws MappingException;
@Override
public NavigableRole getNavigableRole() {
return navigableRole;
}
@Override
public CollectionDataAccess getCacheAccessStrategy() {
return cacheAccessStrategy;
}
@Override
public boolean hasCache() {
return cacheAccessStrategy != null;
}
@Override
public CollectionType getCollectionType() {
return collectionType;
}
protected String getSQLWhereString(String alias) {
return StringHelper.replace( sqlWhereStringTemplate, Template.TEMPLATE, alias );
}
@Override
public String getSQLOrderByString(String alias) {
return hasOrdering()
? orderByTranslation.injectAliases( new StandardOrderByAliasResolver( alias ) )
: "";
}
@Override
public String getManyToManyOrderByString(String alias) {
return hasManyToManyOrdering()
? manyToManyOrderByTranslation.injectAliases( new StandardOrderByAliasResolver( alias ) )
: "";
}
@Override
public FetchMode getFetchMode() {
return fetchMode;
}
@Override
public boolean hasOrdering() {
return hasOrder;
}
@Override
public boolean hasManyToManyOrdering() {
return isManyToMany() && hasManyToManyOrder;
}
@Override
public boolean hasWhere() {
return hasWhere;
}
protected String getSQLDeleteString() {
return sqlDeleteString;
}
protected String getSQLInsertRowString() {
return sqlInsertRowString;
}
protected String getSQLUpdateRowString() {
return sqlUpdateRowString;
}
protected String getSQLDeleteRowString() {
return sqlDeleteRowString;
}
@Override
public Type getKeyType() {
return keyType;
}
@Override
public Type getIndexType() {
return indexType;
}
@Override
public Type getElementType() {
return elementType;
}
@Override
public Class getElementClass() {
return elementClass;
}
@Override
public Object readElement(ResultSet rs, Object owner, String[] aliases, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
return getElementType().nullSafeGet( rs, aliases, session, owner );
}
@Override
public Object readIndex(ResultSet rs, String[] aliases, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
Object index = getIndexType().nullSafeGet( rs, aliases, session, null );
if ( index == null ) {
throw new HibernateException( "null index column for collection: " + navigableRole.getFullPath() );
}
index = decrementIndexByBase( index );
return index;
}
protected Object decrementIndexByBase(Object index) {
if ( baseIndex != 0 ) {
index = (Integer)index - baseIndex;
}
return index;
}
@Override
public Object readIdentifier(ResultSet rs, String alias, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
Object id = getIdentifierType().nullSafeGet( rs, alias, session, null );
if ( id == null ) {
throw new HibernateException( "null identifier column for collection: " + navigableRole.getFullPath() );
}
return id;
}
@Override
public Object readKey(ResultSet rs, String[] aliases, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
final Object hydratedKey = getKeyType().hydrate( rs, aliases, session, null );
return hydratedKey == null ? null : getKeyType().resolve( hydratedKey, session, null );
}
protected int writeKey(PreparedStatement st, Serializable key, int i, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
if ( key == null ) {
throw new NullPointerException( "null key for collection: " + navigableRole.getFullPath() );
}
getKeyType().nullSafeSet( st, key, i, session );
return i + keyColumnAliases.length;
}
protected int writeElement(PreparedStatement st, Object elt, int i, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
getElementType().nullSafeSet( st, elt, i, elementColumnIsSettable, session );
return i + ArrayHelper.countTrue( elementColumnIsSettable );
}
protected int writeIndex(PreparedStatement st, Object index, int i, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
getIndexType().nullSafeSet( st, incrementIndexByBase( index ), i, indexColumnIsSettable, session );
return i + ArrayHelper.countTrue( indexColumnIsSettable );
}
protected Object incrementIndexByBase(Object index) {
if ( baseIndex != 0 ) {
index = (Integer)index + baseIndex;
}
return index;
}
protected int writeElementToWhere(PreparedStatement st, Object elt, int i, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
if ( elementIsPureFormula ) {
throw new AssertionFailure( "cannot use a formula-based element in the where condition" );
}
getElementType().nullSafeSet( st, elt, i, elementColumnIsInPrimaryKey, session );
return i + elementColumnAliases.length;
}
protected int writeIndexToWhere(PreparedStatement st, Object index, int i, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
if ( indexContainsFormula ) {
throw new AssertionFailure( "cannot use a formula-based index in the where condition" );
}
getIndexType().nullSafeSet( st, incrementIndexByBase( index ), i, session );
return i + indexColumnAliases.length;
}
public int writeIdentifier(PreparedStatement st, Object id, int i, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
getIdentifierType().nullSafeSet( st, id, i, session );
return i + 1;
}
@Override
public boolean isPrimitiveArray() {
return isPrimitiveArray;
}
@Override
public boolean isArray() {
return isArray;
}
@Override
public String[] getKeyColumnAliases(String suffix) {
return new Alias( suffix ).toAliasStrings( keyColumnAliases );
}
@Override
public String[] getElementColumnAliases(String suffix) {
return new Alias( suffix ).toAliasStrings( elementColumnAliases );
}
@Override
public String[] getIndexColumnAliases(String suffix) {
if ( hasIndex ) {
return new Alias( suffix ).toAliasStrings( indexColumnAliases );
}
else {
return null;
}
}
@Override
public String getIdentifierColumnAlias(String suffix) {
if ( hasIdentifier ) {
return new Alias( suffix ).toAliasString( identifierColumnAlias );
}
else {
return null;
}
}
@Override
public String getIdentifierColumnName() {
if ( hasIdentifier ) {
return identifierColumnName;
}
else {
return null;
}
}
@Override
public String selectFragment(String alias, String columnSuffix) {
SelectFragment frag = generateSelectFragment( alias, columnSuffix );
appendElementColumns( frag, alias );
appendIndexColumns( frag, alias );
appendIdentifierColumns( frag, alias );
return frag.toFragmentString()
.substring( 2 );
}
protected String generateSelectSizeString(boolean isIntegerIndexed) {
String selectValue = isIntegerIndexed ?
"max(" + getIndexColumnNames()[0] + ") + 1" :
"count(" + getElementColumnNames()[0] + ")";
return new SimpleSelect( dialect )
.setTableName( getTableName() )
.addCondition( getKeyColumnNames(), "=?" )
.addWhereToken( sqlWhereString )
.addColumn( selectValue )
.toStatementString();
}
protected String generateDetectRowByIndexString() {
if ( !hasIndex() ) {
return null;
}
return new SimpleSelect( dialect )
.setTableName( getTableName() )
.addCondition( getKeyColumnNames(), "=?" )
.addCondition( getIndexColumnNames(), "=?" )
.addCondition( indexFormulas, "=?" )
.addWhereToken( sqlWhereString )
.addColumn( "1" )
.toStatementString();
}
protected String generateSelectRowByIndexString() {
if ( !hasIndex() ) {
return null;
}
return new SimpleSelect( dialect )
.setTableName( getTableName() )
.addCondition( getKeyColumnNames(), "=?" )
.addCondition( getIndexColumnNames(), "=?" )
.addCondition( indexFormulas, "=?" )
.addWhereToken( sqlWhereString )
.addColumns( getElementColumnNames(), elementColumnAliases )
.addColumns( indexFormulas, indexColumnAliases )
.toStatementString();
}
protected String generateDetectRowByElementString() {
return new SimpleSelect( dialect )
.setTableName( getTableName() )
.addCondition( getKeyColumnNames(), "=?" )
.addCondition( getElementColumnNames(), "=?" )
.addCondition( elementFormulas, "=?" )
.addWhereToken( sqlWhereString )
.addColumn( "1" )
.toStatementString();
}
protected SelectFragment generateSelectFragment(String alias, String columnSuffix) {
return new SelectFragment()
.setSuffix( columnSuffix )
.addColumns( alias, keyColumnNames, keyColumnAliases );
}
protected void appendElementColumns(SelectFragment frag, String elemAlias) {
for ( int i = 0; i < elementColumnIsSettable.length; i++ ) {
if ( elementColumnIsSettable[i] ) {
frag.addColumnTemplate( elemAlias, elementColumnReaderTemplates[i], elementColumnAliases[i] );
}
else {
frag.addFormula( elemAlias, elementFormulaTemplates[i], elementColumnAliases[i] );
}
}
}
protected void appendIndexColumns(SelectFragment frag, String alias) {
if ( hasIndex ) {
for ( int i = 0; i < indexColumnIsGettable.length; i++ ) {
if ( indexColumnIsGettable[i] ) {
frag.addColumn( alias, indexColumnNames[i], indexColumnAliases[i] );
}
else {
frag.addFormula( alias, indexFormulaTemplates[i], indexColumnAliases[i] );
}
}
}
}
protected void appendIdentifierColumns(SelectFragment frag, String alias) {
if ( hasIdentifier ) {
frag.addColumn( alias, identifierColumnName, identifierColumnAlias );
}
}
@Override
public String[] getIndexColumnNames() {
return indexColumnNames;
}
@Override
public String[] getIndexFormulas() {
return indexFormulas;
}
@Override
public String[] getIndexColumnNames(String alias) {
return qualify( alias, indexColumnNames, indexFormulaTemplates );
}
@Override
public String[] getElementColumnNames(String alias) {
return qualify( alias, elementColumnNames, elementFormulaTemplates );
}
private static String[] qualify(String alias, String[] columnNames, String[] formulaTemplates) {
int span = columnNames.length;
String[] result = new String[span];
for ( int i = 0; i < span; i++ ) {
if ( columnNames[i] == null ) {
result[i] = StringHelper.replace( formulaTemplates[i], Template.TEMPLATE, alias );
}
else {
result[i] = StringHelper.qualify( alias, columnNames[i] );
}
}
return result;
}
@Override
public String[] getElementColumnNames() {
return elementColumnNames;
}
@Override
public String[] getKeyColumnNames() {
return keyColumnNames;
}
@Override
public boolean hasIndex() {
return hasIndex;
}
@Override
public boolean isLazy() {
return isLazy;
}
@Override
public boolean isInverse() {
return isInverse;
}
@Override
public String getTableName() {
return qualifiedTableName;
}
private BasicBatchKey removeBatchKey;
@Override
public void remove(Serializable id, SharedSessionContractImplementor session) throws HibernateException {
if ( !isInverse && isRowDeleteEnabled() ) {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Deleting collection: %s",
MessageHelper.collectionInfoString( this, id, getFactory() ) );
}
try {
int offset = 1;
PreparedStatement st = null;
Expectation expectation = Expectations.appropriateExpectation( getDeleteAllCheckStyle() );
boolean callable = isDeleteAllCallable();
boolean useBatch = expectation.canBeBatched();
String sql = getSQLDeleteString();
if ( useBatch ) {
if ( removeBatchKey == null ) {
removeBatchKey = new BasicBatchKey(
getRole() + "#REMOVE",
expectation
);
}
st = session
.getJdbcCoordinator()
.getBatch( removeBatchKey )
.getBatchStatement( sql, callable );
}
else {
st = session
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql, callable );
}
try {
offset += expectation.prepare( st );
writeKey( st, id, offset, session );
if ( useBatch ) {
session
.getJdbcCoordinator()
.getBatch( removeBatchKey )
.addToBatch();
}
else {
expectation.verifyOutcome( session.getJdbcCoordinator().getResultSetReturn().executeUpdate( st ), st, -1 );
}
}
catch ( SQLException sqle ) {
if ( useBatch ) {
session.getJdbcCoordinator().abortBatch();
}
throw sqle;
}
finally {
if ( !useBatch ) {
session.getJdbcCoordinator().getResourceRegistry().release( st );
session.getJdbcCoordinator().afterStatementExecution();
}
}
LOG.debug( "Done deleting collection" );
}
catch ( SQLException sqle ) {
throw sqlExceptionHelper.convert(
sqle,
"could not delete collection: " +
MessageHelper.collectionInfoString( this, id, getFactory() ),
getSQLDeleteString()
);
}
}
}
protected BasicBatchKey recreateBatchKey;
@Override
public void recreate(PersistentCollection collection, Serializable id, SharedSessionContractImplementor session)
throws HibernateException {
if ( isInverse ) {
return;
}
if ( !isRowInsertEnabled() ) {
return;
}
if ( LOG.isDebugEnabled() ) {
LOG.debugf(
"Inserting collection: %s",
MessageHelper.collectionInfoString( this, collection, id, session )
);
}
try {
Iterator entries = collection.entries( this );
if ( entries.hasNext() ) {
Expectation expectation = Expectations.appropriateExpectation( getInsertCheckStyle() );
collection.preInsert( this );
int i = 0;
int count = 0;
while ( entries.hasNext() ) {
final Object entry = entries.next();
if ( collection.entryExists( entry, i ) ) {
int offset = 1;
PreparedStatement st = null;
boolean callable = isInsertCallable();
boolean useBatch = expectation.canBeBatched();
String sql = getSQLInsertRowString();
if ( useBatch ) {
if ( recreateBatchKey == null ) {
recreateBatchKey = new BasicBatchKey(
getRole() + "#RECREATE",
expectation
);
}
st = session
.getJdbcCoordinator()
.getBatch( recreateBatchKey )
.getBatchStatement( sql, callable );
}
else {
st = session
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql, callable );
}
try {
offset += expectation.prepare( st );
int loc = writeKey( st, id, offset, session );
if ( hasIdentifier ) {
loc = writeIdentifier( st, collection.getIdentifier( entry, i ), loc, session );
}
if ( hasIndex ) {
loc = writeIndex( st, collection.getIndex( entry, i, this ), loc, session );
}
loc = writeElement( st, collection.getElement( entry ), loc, session );
if ( useBatch ) {
session
.getJdbcCoordinator()
.getBatch( recreateBatchKey )
.addToBatch();
}
else {
expectation.verifyOutcome( session.getJdbcCoordinator().getResultSetReturn().executeUpdate( st ), st, -1 );
}
collection.afterRowInsert( this, entry, i );
count++;
}
catch ( SQLException sqle ) {
if ( useBatch ) {
session.getJdbcCoordinator().abortBatch();
}
throw sqle;
}
finally {
if ( !useBatch ) {
session.getJdbcCoordinator().getResourceRegistry().release( st );
session.getJdbcCoordinator().afterStatementExecution();
}
}
}
i++;
}
LOG.debugf( "Done inserting collection: %s rows inserted", count );
}
else {
LOG.debug( "Collection was empty" );
}
}
catch ( SQLException sqle ) {
throw sqlExceptionHelper.convert(
sqle,
"could not insert collection: " +
MessageHelper.collectionInfoString( this, collection, id, session ),
getSQLInsertRowString()
);
}
}
protected boolean isRowDeleteEnabled() {
return true;
}
private BasicBatchKey deleteBatchKey;
@Override
public void deleteRows(PersistentCollection collection, Serializable id, SharedSessionContractImplementor session)
throws HibernateException {
if ( isInverse ) {
return;
}
if ( !isRowDeleteEnabled() ) {
return;
}
if ( LOG.isDebugEnabled() ) {
LOG.debugf(
"Deleting rows of collection: %s",
MessageHelper.collectionInfoString( this, collection, id, session )
);
}
boolean deleteByIndex = !isOneToMany() && hasIndex && !indexContainsFormula;
final Expectation expectation = Expectations.appropriateExpectation( getDeleteCheckStyle() );
try {
Iterator deletes = collection.getDeletes( this, !deleteByIndex );
if ( deletes.hasNext() ) {
int offset = 1;
int count = 0;
while ( deletes.hasNext() ) {
PreparedStatement st = null;
boolean callable = isDeleteCallable();
boolean useBatch = expectation.canBeBatched();
String sql = getSQLDeleteRowString();
if ( useBatch ) {
if ( deleteBatchKey == null ) {
deleteBatchKey = new BasicBatchKey(
getRole() + "#DELETE",
expectation
);
}
st = session
.getJdbcCoordinator()
.getBatch( deleteBatchKey )
.getBatchStatement( sql, callable );
}
else {
st = session
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql, callable );
}
try {
expectation.prepare( st );
Object entry = deletes.next();
int loc = offset;
if ( hasIdentifier ) {
writeIdentifier( st, entry, loc, session );
}
else {
loc = writeKey( st, id, loc, session );
if ( deleteByIndex ) {
writeIndexToWhere( st, entry, loc, session );
}
else {
writeElementToWhere( st, entry, loc, session );
}
}
if ( useBatch ) {
session
.getJdbcCoordinator()
.getBatch( deleteBatchKey )
.addToBatch();
}
else {
expectation.verifyOutcome( session.getJdbcCoordinator().getResultSetReturn().executeUpdate( st ), st, -1 );
}
count++;
}
catch ( SQLException sqle ) {
if ( useBatch ) {
session.getJdbcCoordinator().abortBatch();
}
throw sqle;
}
finally {
if ( !useBatch ) {
session.getJdbcCoordinator().getResourceRegistry().release( st );
session.getJdbcCoordinator().afterStatementExecution();
}
}
LOG.debugf( "Done deleting collection rows: %s deleted", count );
}
}
else {
LOG.debug( "No rows to delete" );
}
}
catch ( SQLException sqle ) {
throw sqlExceptionHelper.convert(
sqle,
"could not delete collection rows: " +
MessageHelper.collectionInfoString( this, collection, id, session ),
getSQLDeleteRowString()
);
}
}
protected boolean isRowInsertEnabled() {
return true;
}
private BasicBatchKey insertBatchKey;
@Override
public void insertRows(PersistentCollection collection, Serializable id, SharedSessionContractImplementor session)
throws HibernateException {
if ( isInverse ) {
return;
}
if ( !isRowInsertEnabled() ) {
return;
}
if ( LOG.isDebugEnabled() ) {
LOG.debugf(
"Inserting rows of collection: %s",
MessageHelper.collectionInfoString( this, collection, id, session )
);
}
try {
collection.preInsert( this );
Iterator entries = collection.entries( this );
Expectation expectation = Expectations.appropriateExpectation( getInsertCheckStyle() );
boolean callable = isInsertCallable();
boolean useBatch = expectation.canBeBatched();
String sql = getSQLInsertRowString();
int i = 0;
int count = 0;
while ( entries.hasNext() ) {
int offset = 1;
Object entry = entries.next();
PreparedStatement st = null;
if ( collection.needsInserting( entry, i, elementType ) ) {
if ( useBatch ) {
if ( insertBatchKey == null ) {
insertBatchKey = new BasicBatchKey(
getRole() + "#INSERT",
expectation
);
}
if ( st == null ) {
st = session
.getJdbcCoordinator()
.getBatch( insertBatchKey )
.getBatchStatement( sql, callable );
}
}
else {
st = session
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql, callable );
}
try {
offset += expectation.prepare( st );
offset = writeKey( st, id, offset, session );
if ( hasIdentifier ) {
offset = writeIdentifier( st, collection.getIdentifier( entry, i ), offset, session );
}
if ( hasIndex ) {
offset = writeIndex( st, collection.getIndex( entry, i, this ), offset, session );
}
writeElement( st, collection.getElement( entry ), offset, session );
if ( useBatch ) {
session.getJdbcCoordinator().getBatch( insertBatchKey ).addToBatch();
}
else {
expectation.verifyOutcome( session.getJdbcCoordinator().getResultSetReturn().executeUpdate( st ), st, -1 );
}
collection.afterRowInsert( this, entry, i );
count++;
}
catch ( SQLException sqle ) {
if ( useBatch ) {
session.getJdbcCoordinator().abortBatch();
}
throw sqle;
}
finally {
if ( !useBatch ) {
session.getJdbcCoordinator().getResourceRegistry().release( st );
session.getJdbcCoordinator().afterStatementExecution();
}
}
}
i++;
}
LOG.debugf( "Done inserting rows: %s inserted", count );
}
catch ( SQLException sqle ) {
throw sqlExceptionHelper.convert(
sqle,
"could not insert collection rows: " +
MessageHelper.collectionInfoString( this, collection, id, session ),
getSQLInsertRowString()
);
}
}
@Override
public String getRole() {
return navigableRole.getFullPath();
}
public String getOwnerEntityName() {
return entityName;
}
@Override
public EntityPersister getOwnerEntityPersister() {
return ownerPersister;
}
@Override
public IdentifierGenerator getIdentifierGenerator() {
return identifierGenerator;
}
@Override
public Type getIdentifierType() {
return identifierType;
}
@Override
public boolean hasOrphanDelete() {
return hasOrphanDelete;
}
@Override
public Type toType(String propertyName) throws QueryException {
if ( "index".equals( propertyName ) ) {
return indexType;
}
return elementPropertyMapping.toType( propertyName );
}
@Override
public abstract boolean isManyToMany();
@Override
public String getManyToManyFilterFragment(String alias, Map enabledFilters) {
StringBuilder buffer = new StringBuilder();
manyToManyFilterHelper.render( buffer, elementPersister.getFilterAliasGenerator(alias), enabledFilters );
if ( manyToManyWhereString != null ) {
buffer.append( " and " )
.append( StringHelper.replace( manyToManyWhereTemplate, Template.TEMPLATE, alias ) );
}
return buffer.toString();
}
@Override
public String[] toColumns(String alias, String propertyName) throws QueryException {
if ( "index".equals( propertyName ) ) {
return qualify( alias, indexColumnNames, indexFormulaTemplates );
}
return elementPropertyMapping.toColumns( alias, propertyName );
}
private String[] indexFragments;
@Override
public String[] toColumns(String propertyName) throws QueryException {
if ( "index".equals( propertyName ) ) {
if ( indexFragments == null ) {
String[] tmp = new String[indexColumnNames.length];
for ( int i = 0; i < indexColumnNames.length; i++ ) {
tmp[i] = indexColumnNames[i] == null
? indexFormulas[i]
: indexColumnNames[i];
indexFragments = tmp;
}
}
return indexFragments;
}
return elementPropertyMapping.toColumns( propertyName );
}
@Override
public Type getType() {
return elementPropertyMapping.getType();
}
@Override
public String getName() {
return getRole();
}
@Override
public EntityPersister getElementPersister() {
if ( elementPersister == null ) {
throw new AssertionFailure( "not an association" );
}
return elementPersister;
}
@Override
public boolean isCollection() {
return true;
}
@Override
public Serializable[] getCollectionSpaces() {
return spaces;
}
protected abstract String generateDeleteString();
protected abstract String generateDeleteRowString();
protected abstract String generateUpdateRowString();
protected abstract String generateInsertRowString();
@Override
public void updateRows(PersistentCollection collection, Serializable id, SharedSessionContractImplementor session)
throws HibernateException {
if ( !isInverse && collection.isRowUpdatePossible() ) {
LOG.debugf( "Updating rows of collection: %s#%s", navigableRole.getFullPath(), id );
int count = doUpdateRows( id, collection, session );
LOG.debugf( "Done updating rows: %s updated", count );
}
}
protected abstract int doUpdateRows(Serializable key, PersistentCollection collection, SharedSessionContractImplementor session)
throws HibernateException;
@Override
public void processQueuedOps(PersistentCollection collection, Serializable key, SharedSessionContractImplementor session)
throws HibernateException {
if ( collection.hasQueuedOperations() ) {
doProcessQueuedOps( collection, key, session );
}
}
@Deprecated
protected void doProcessQueuedOps(PersistentCollection collection, Serializable key,
int nextIndex, SharedSessionContractImplementor session)
throws HibernateException {
doProcessQueuedOps( collection, key, session );
}
protected abstract void doProcessQueuedOps(PersistentCollection collection, Serializable key, SharedSessionContractImplementor session)
throws HibernateException;
@Override
public CollectionMetadata getCollectionMetadata() {
return this;
}
@Override
public SessionFactoryImplementor getFactory() {
return factory;
}
protected String filterFragment(String alias) throws MappingException {
return hasWhere() ? " and " + getSQLWhereString( alias ) : "";
}
protected String filterFragment(String alias, Set<String> treatAsDeclarations) throws MappingException {
return hasWhere() ? " and " + getSQLWhereString( alias ) : "";
}
@Override
public String filterFragment(String alias, Map enabledFilters) throws MappingException {
StringBuilder sessionFilterFragment = new StringBuilder();
filterHelper.render( sessionFilterFragment, getFilterAliasGenerator(alias), enabledFilters );
return sessionFilterFragment.append( filterFragment( alias ) ).toString();
}
@Override
public String filterFragment(
String alias,
Map enabledFilters,
Set<String> treatAsDeclarations) {
StringBuilder sessionFilterFragment = new StringBuilder();
filterHelper.render( sessionFilterFragment, getFilterAliasGenerator(alias), enabledFilters );
return sessionFilterFragment.append( filterFragment( alias, treatAsDeclarations ) ).toString();
}
@Override
public String oneToManyFilterFragment(String alias) throws MappingException {
return "";
}
@Override
public String oneToManyFilterFragment(String alias, Set<String> treatAsDeclarations) {
return oneToManyFilterFragment( alias );
}
protected boolean isInsertCallable() {
return insertCallable;
}
protected ExecuteUpdateResultCheckStyle getInsertCheckStyle() {
return insertCheckStyle;
}
protected boolean isUpdateCallable() {
return updateCallable;
}
protected ExecuteUpdateResultCheckStyle getUpdateCheckStyle() {
return updateCheckStyle;
}
protected boolean isDeleteCallable() {
return deleteCallable;
}
protected ExecuteUpdateResultCheckStyle getDeleteCheckStyle() {
return deleteCheckStyle;
}
protected boolean isDeleteAllCallable() {
return deleteAllCallable;
}
protected ExecuteUpdateResultCheckStyle getDeleteAllCheckStyle() {
return deleteAllCheckStyle;
}
@Override
public String toString() {
return StringHelper.unqualify( getClass().getName() ) + '(' + navigableRole.getFullPath() + ')';
}
@Override
public boolean isVersioned() {
return isVersioned && getOwnerEntityPersister().isVersioned();
}
protected SQLExceptionConverter getSQLExceptionConverter() {
return getSQLExceptionHelper().getSqlExceptionConverter();
}
protected SqlExceptionHelper getSQLExceptionHelper() {
return sqlExceptionHelper;
}
@Override
public CacheEntryStructure getCacheEntryStructure() {
return cacheEntryStructure;
}
@Override
public boolean isAffectedByEnabledFilters(SharedSessionContractImplementor session) {
return filterHelper.isAffectedBy( session.getLoadQueryInfluencers().getEnabledFilters() ) ||
( isManyToMany() && manyToManyFilterHelper.isAffectedBy( session.getLoadQueryInfluencers().getEnabledFilters() ) );
}
public boolean isSubselectLoadable() {
return subselectLoadable;
}
@Override
public boolean isMutable() {
return isMutable;
}
@Override
public String[] getCollectionPropertyColumnAliases(String propertyName, String suffix) {
String[] rawAliases = (String[]) collectionPropertyColumnAliases.get( propertyName );
if ( rawAliases == null ) {
return null;
}
String[] result = new String[rawAliases.length];
for ( int i = 0; i < rawAliases.length; i++ ) {
result[i] = new Alias( suffix ).toUnquotedAliasString( rawAliases[i] );
}
return result;
}
public void initCollectionPropertyMap() {
initCollectionPropertyMap( "key", keyType, keyColumnAliases, keyColumnNames );
initCollectionPropertyMap( "element", elementType, elementColumnAliases, elementColumnNames );
if ( hasIndex ) {
initCollectionPropertyMap( "index", indexType, indexColumnAliases, indexColumnNames );
}
if ( hasIdentifier ) {
initCollectionPropertyMap(
"id",
identifierType,
new String[] { identifierColumnAlias },
new String[] { identifierColumnName } );
}
}
private void initCollectionPropertyMap(String aliasName, Type type, String[] columnAliases, String[] columnNames) {
collectionPropertyColumnAliases.put( aliasName, columnAliases );
collectionPropertyColumnNames.put( aliasName, columnNames );
if ( type.isComponentType() ) {
CompositeType ct = (CompositeType) type;
String[] propertyNames = ct.getPropertyNames();
for ( int i = 0; i < propertyNames.length; i++ ) {
String name = propertyNames[i];
collectionPropertyColumnAliases.put( aliasName + "." + name, columnAliases[i] );
collectionPropertyColumnNames.put( aliasName + "." + name, columnNames[i] );
}
}
}
@Override
public int getSize(Serializable key, SharedSessionContractImplementor session) {
try {
PreparedStatement st = session
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sqlSelectSizeString );
try {
getKeyType().nullSafeSet( st, key, 1, session );
ResultSet rs = session.getJdbcCoordinator().getResultSetReturn().extract( st );
try {
return rs.next() ? rs.getInt( 1 ) - baseIndex : 0;
}
finally {
session.getJdbcCoordinator().getResourceRegistry().release( rs, st );
}
}
finally {
session.getJdbcCoordinator().getResourceRegistry().release( st );
session.getJdbcCoordinator().afterStatementExecution();
}
}
catch ( SQLException sqle ) {
throw getSQLExceptionHelper().convert(
sqle,
"could not retrieve collection size: " +
MessageHelper.collectionInfoString( this, key, getFactory() ),
sqlSelectSizeString
);
}
}
@Override
public boolean indexExists(Serializable key, Object index, SharedSessionContractImplementor session) {
return exists( key, incrementIndexByBase( index ), getIndexType(), sqlDetectRowByIndexString, session );
}
@Override
public boolean elementExists(Serializable key, Object element, SharedSessionContractImplementor session) {
return exists( key, element, getElementType(), sqlDetectRowByElementString, session );
}
private boolean exists(Serializable key, Object indexOrElement, Type indexOrElementType, String sql, SharedSessionContractImplementor session) {
try {
PreparedStatement st = session
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql );
try {
getKeyType().nullSafeSet( st, key, 1, session );
indexOrElementType.nullSafeSet( st, indexOrElement, keyColumnNames.length + 1, session );
ResultSet rs = session.getJdbcCoordinator().getResultSetReturn().extract( st );
try {
return rs.next();
}
finally {
session.getJdbcCoordinator().getResourceRegistry().release( rs, st );
}
}
catch ( TransientObjectException e ) {
return false;
}
finally {
session.getJdbcCoordinator().getResourceRegistry().release( st );
session.getJdbcCoordinator().afterStatementExecution();
}
}
catch ( SQLException sqle ) {
throw getSQLExceptionHelper().convert(
sqle,
"could not check row existence: " +
MessageHelper.collectionInfoString( this, key, getFactory() ),
sqlSelectSizeString
);
}
}
@Override
public Object getElementByIndex(Serializable key, Object index, SharedSessionContractImplementor session, Object owner) {
try {
PreparedStatement st = session
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sqlSelectRowByIndexString );
try {
getKeyType().nullSafeSet( st, key, 1, session );
getIndexType().nullSafeSet( st, incrementIndexByBase( index ), keyColumnNames.length + 1, session );
ResultSet rs = session.getJdbcCoordinator().getResultSetReturn().extract( st );
try {
if ( rs.next() ) {
return getElementType().nullSafeGet( rs, elementColumnAliases, session, owner );
}
else {
return null;
}
}
finally {
session.getJdbcCoordinator().getResourceRegistry().release( rs, st );
}
}
finally {
session.getJdbcCoordinator().getResourceRegistry().release( st );
session.getJdbcCoordinator().afterStatementExecution();
}
}
catch ( SQLException sqle ) {
throw getSQLExceptionHelper().convert(
sqle,
"could not read row: " +
MessageHelper.collectionInfoString( this, key, getFactory() ),
sqlSelectSizeString
);
}
}
@Override
public boolean () {
return isExtraLazy;
}
protected Dialect getDialect() {
return dialect;
}
public CollectionInitializer getInitializer() {
return initializer;
}
@Override
public int getBatchSize() {
return batchSize;
}
@Override
public String getMappedByProperty() {
return mappedByProperty;
}
private class StandardOrderByAliasResolver implements OrderByAliasResolver {
private final String rootAlias;
private StandardOrderByAliasResolver(String rootAlias) {
this.rootAlias = rootAlias;
}
@Override
public String resolveTableAlias(String columnReference) {
if ( elementPersister == null ) {
return rootAlias;
}
else {
return ( (Loadable) elementPersister ).getTableAliasForColumn( columnReference, rootAlias );
}
}
}
public abstract FilterAliasGenerator getFilterAliasGenerator(final String rootAlias);
@Override
public CollectionPersister getCollectionPersister() {
return this;
}
@Override
public CollectionIndexDefinition getIndexDefinition() {
if ( ! hasIndex() ) {
return null;
}
return new CollectionIndexDefinition() {
@Override
public CollectionDefinition getCollectionDefinition() {
return AbstractCollectionPersister.this;
}
@Override
public Type getType() {
return getIndexType();
}
@Override
public EntityDefinition toEntityDefinition() {
if ( !getType().isEntityType() ) {
throw new IllegalStateException( "Cannot treat collection index type as entity" );
}
return (EntityPersister) ( (AssociationType) getIndexType() ).getAssociatedJoinable( getFactory() );
}
@Override
public CompositionDefinition toCompositeDefinition() {
if ( ! getType().isComponentType() ) {
throw new IllegalStateException( "Cannot treat collection index type as composite" );
}
return new CompositeCollectionElementDefinition() {
@Override
public String getName() {
return "index";
}
@Override
public CompositeType getType() {
return (CompositeType) getIndexType();
}
@Override
public boolean isNullable() {
return false;
}
@Override
public AttributeSource getSource() {
return getOwnerEntityPersister();
}
@Override
public Iterable<AttributeDefinition> getAttributes() {
return CompositionSingularSubAttributesHelper.getCompositeCollectionIndexSubAttributes( this );
}
@Override
public CollectionDefinition getCollectionDefinition() {
return AbstractCollectionPersister.this;
}
};
}
@Override
public AnyMappingDefinition toAnyMappingDefinition() {
final Type type = getType();
if ( ! type.isAnyType() ) {
throw new IllegalStateException( "Cannot treat collection index type as ManyToAny" );
}
return new StandardAnyTypeDefinition( (AnyType) type, isLazy() || isExtraLazy() );
}
};
}
@Override
public CollectionElementDefinition getElementDefinition() {
return new CollectionElementDefinition() {
@Override
public CollectionDefinition getCollectionDefinition() {
return AbstractCollectionPersister.this;
}
@Override
public Type getType() {
return getElementType();
}
@Override
public AnyMappingDefinition toAnyMappingDefinition() {
final Type type = getType();
if ( ! type.isAnyType() ) {
throw new IllegalStateException( "Cannot treat collection element type as ManyToAny" );
}
return new StandardAnyTypeDefinition( (AnyType) type, isLazy() || isExtraLazy() );
}
@Override
public EntityDefinition toEntityDefinition() {
if ( !getType().isEntityType() ) {
throw new IllegalStateException( "Cannot treat collection element type as entity" );
}
return getElementPersister();
}
@Override
public CompositeCollectionElementDefinition toCompositeElementDefinition() {
if ( ! getType().isComponentType() ) {
throw new IllegalStateException( "Cannot treat entity collection element type as composite" );
}
return new CompositeCollectionElementDefinition() {
@Override
public String getName() {
return "";
}
@Override
public CompositeType getType() {
return (CompositeType) getElementType();
}
@Override
public boolean isNullable() {
return false;
}
@Override
public AttributeSource getSource() {
return getOwnerEntityPersister();
}
@Override
public Iterable<AttributeDefinition> getAttributes() {
return CompositionSingularSubAttributesHelper.getCompositeCollectionElementSubAttributes( this );
}
@Override
public CollectionDefinition getCollectionDefinition() {
return AbstractCollectionPersister.this;
}
};
}
};
}
}