Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.hibernate.persister.entity.AbstractEntityPersister Maven / Gradle / Ivy
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.persister.entity;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.AssertionFailure;
import org.hibernate.EntityMode;
import org.hibernate.FetchMode;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.Session;
import org.hibernate.StaleObjectStateException;
import org.hibernate.StaleStateException;
import org.hibernate.bytecode.instrumentation.spi.FieldInterceptor;
import org.hibernate.bytecode.instrumentation.spi.LazyPropertyInitializer;
import org.hibernate.bytecode.spi.EntityInstrumentationMetadata;
import org.hibernate.cache.spi.CacheKey;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.cache.spi.entry.CacheEntry;
import org.hibernate.cache.spi.entry.CacheEntryStructure;
import org.hibernate.cache.spi.entry.ReferenceCacheEntryImpl;
import org.hibernate.cache.spi.entry.StandardCacheEntryImpl;
import org.hibernate.cache.spi.entry.StructuredCacheEntry;
import org.hibernate.cache.spi.entry.UnstructuredCacheEntry;
import org.hibernate.dialect.lock.LockingStrategy;
import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.engine.internal.CacheHelper;
import org.hibernate.engine.internal.StatefulPersistenceContext;
import org.hibernate.engine.internal.Versioning;
import org.hibernate.engine.jdbc.batch.internal.BasicBatchKey;
import org.hibernate.engine.spi.CachedNaturalIdValueSource;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.CascadeStyles;
import org.hibernate.engine.spi.CascadingActions;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.PersistenceContext.NaturalIdHelper;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.ValueInclusion;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PostInsertIdentifierGenerator;
import org.hibernate.id.PostInsertIdentityPersister;
import org.hibernate.id.insert.Binder;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.FilterConfiguration;
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.jdbc.TooManyRowsAffectedException;
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
import org.hibernate.loader.entity.CascadeEntityLoader;
import org.hibernate.loader.entity.EntityLoader;
import org.hibernate.loader.entity.UniqueEntityLoader;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.metamodel.binding.AssociationAttributeBinding;
import org.hibernate.metamodel.binding.AttributeBinding;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.binding.SimpleValueBinding;
import org.hibernate.metamodel.binding.SingularAttributeBinding;
import org.hibernate.metamodel.relational.DerivedValue;
import org.hibernate.metamodel.relational.Value;
import org.hibernate.persister.walking.internal.EntityIdentifierDefinitionHelper;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.EntityIdentifierDefinition;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.property.BackrefPropertyAccessor;
import org.hibernate.sql.Alias;
import org.hibernate.sql.Delete;
import org.hibernate.sql.Insert;
import org.hibernate.sql.JoinFragment;
import org.hibernate.sql.JoinType;
import org.hibernate.sql.Select;
import org.hibernate.sql.SelectFragment;
import org.hibernate.sql.SimpleSelect;
import org.hibernate.sql.Template;
import org.hibernate.sql.Update;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.InDatabaseValueGenerationStrategy;
import org.hibernate.tuple.InMemoryValueGenerationStrategy;
import org.hibernate.tuple.NonIdentifierAttribute;
import org.hibernate.tuple.ValueGeneration;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.tuple.entity.EntityTuplizer;
import org.hibernate.type.AssociationType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.type.TypeHelper;
import org.hibernate.type.VersionType;
import org.jboss.logging.Logger;
/**
* Basic functionality for persisting an entity via JDBC
* through either generated or custom SQL
*
* @author Gavin King
*/
public abstract class AbstractEntityPersister
implements OuterJoinLoadable, Queryable, ClassMetadata, UniqueKeyLoadable,
SQLLoadable, LazyPropertyInitializer, PostInsertIdentityPersister, Lockable {
private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, AbstractEntityPersister.class.getName() );
public static final String ENTITY_CLASS = "class";
// moved up from AbstractEntityPersister ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private final SessionFactoryImplementor factory;
private final EntityRegionAccessStrategy cacheAccessStrategy;
private final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy;
private final boolean isLazyPropertiesCacheable;
private final CacheEntryHelper cacheEntryHelper;
private final EntityMetamodel entityMetamodel;
private final EntityTuplizer entityTuplizer;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private final String[] rootTableKeyColumnNames;
private final String[] rootTableKeyColumnReaders;
private final String[] rootTableKeyColumnReaderTemplates;
private final String[] identifierAliases;
private final int identifierColumnSpan;
private final String versionColumnName;
private final boolean hasFormulaProperties;
private final int batchSize;
private final boolean hasSubselectLoadableCollections;
protected final String rowIdName;
private final Set lazyProperties;
// The optional SQL string defined in the where attribute
private final String sqlWhereString;
private final String sqlWhereStringTemplate;
//information about properties of this class,
//including inherited properties
//(only really needed for updatable/insertable properties)
private final int[] propertyColumnSpans;
private final String[] propertySubclassNames;
private final String[][] propertyColumnAliases;
private final String[][] propertyColumnNames;
private final String[][] propertyColumnFormulaTemplates;
private final String[][] propertyColumnReaderTemplates;
private final String[][] propertyColumnWriters;
private final boolean[][] propertyColumnUpdateable;
private final boolean[][] propertyColumnInsertable;
private final boolean[] propertyUniqueness;
private final boolean[] propertySelectable;
private final List lobProperties = new ArrayList();
//information about lazy properties of this class
private final String[] lazyPropertyNames;
private final int[] lazyPropertyNumbers;
private final Type[] lazyPropertyTypes;
private final String[][] lazyPropertyColumnAliases;
//information about all properties in class hierarchy
private final String[] subclassPropertyNameClosure;
private final String[] subclassPropertySubclassNameClosure;
private final Type[] subclassPropertyTypeClosure;
private final String[][] subclassPropertyFormulaTemplateClosure;
private final String[][] subclassPropertyColumnNameClosure;
private final String[][] subclassPropertyColumnReaderClosure;
private final String[][] subclassPropertyColumnReaderTemplateClosure;
private final FetchMode[] subclassPropertyFetchModeClosure;
private final boolean[] subclassPropertyNullabilityClosure;
private final boolean[] propertyDefinedOnSubclass;
private final int[][] subclassPropertyColumnNumberClosure;
private final int[][] subclassPropertyFormulaNumberClosure;
private final CascadeStyle[] subclassPropertyCascadeStyleClosure;
//information about all columns/formulas in class hierarchy
private final String[] subclassColumnClosure;
private final boolean[] subclassColumnLazyClosure;
private final String[] subclassColumnAliasClosure;
private final boolean[] subclassColumnSelectableClosure;
private final String[] subclassColumnReaderTemplateClosure;
private final String[] subclassFormulaClosure;
private final String[] subclassFormulaTemplateClosure;
private final String[] subclassFormulaAliasClosure;
private final boolean[] subclassFormulaLazyClosure;
// dynamic filters attached to the class-level
private final FilterHelper filterHelper;
private final Set affectingFetchProfileNames = new HashSet();
private final Map uniqueKeyLoaders = new HashMap();
private final Map lockers = new HashMap();
private final Map loaders = new HashMap();
// SQL strings
private String sqlVersionSelectString;
private String sqlSnapshotSelectString;
private String sqlLazySelectString;
private String sqlIdentityInsertString;
private String sqlUpdateByRowIdString;
private String sqlLazyUpdateByRowIdString;
private String[] sqlDeleteStrings;
private String[] sqlInsertStrings;
private String[] sqlUpdateStrings;
private String[] sqlLazyUpdateStrings;
private String sqlInsertGeneratedValuesSelectString;
private String sqlUpdateGeneratedValuesSelectString;
//Custom SQL (would be better if these were private)
protected boolean[] insertCallable;
protected boolean[] updateCallable;
protected boolean[] deleteCallable;
protected String[] customSQLInsert;
protected String[] customSQLUpdate;
protected String[] customSQLDelete;
protected ExecuteUpdateResultCheckStyle[] insertResultCheckStyles;
protected ExecuteUpdateResultCheckStyle[] updateResultCheckStyles;
protected ExecuteUpdateResultCheckStyle[] deleteResultCheckStyles;
private InsertGeneratedIdentifierDelegate identityDelegate;
private boolean[] tableHasColumns;
private final String loaderName;
private UniqueEntityLoader queryLoader;
private final String temporaryIdTableName;
private final String temporaryIdTableDDL;
private final Map subclassPropertyAliases = new HashMap();
private final Map subclassPropertyColumnNames = new HashMap();
protected final BasicEntityPropertyMapping propertyMapping;
protected void addDiscriminatorToInsert(Insert insert) {}
protected void addDiscriminatorToSelect(SelectFragment select, String name, String suffix) {}
protected abstract int[] getSubclassColumnTableNumberClosure();
protected abstract int[] getSubclassFormulaTableNumberClosure();
public abstract String getSubclassTableName(int j);
protected abstract String[] getSubclassTableKeyColumns(int j);
protected abstract boolean isClassOrSuperclassTable(int j);
protected abstract int getSubclassTableSpan();
protected abstract int getTableSpan();
protected abstract boolean isTableCascadeDeleteEnabled(int j);
protected abstract String getTableName(int j);
protected abstract String[] getKeyColumns(int j);
protected abstract boolean isPropertyOfTable(int property, int j);
protected abstract int[] getPropertyTableNumbersInSelect();
protected abstract int[] getPropertyTableNumbers();
protected abstract int getSubclassPropertyTableNumber(int i);
protected abstract String filterFragment(String alias) throws MappingException;
protected abstract String filterFragment(String alias, Set treatAsDeclarations);
private static final String DISCRIMINATOR_ALIAS = "clazz_";
public String getDiscriminatorColumnName() {
return DISCRIMINATOR_ALIAS;
}
public String getDiscriminatorColumnReaders() {
return DISCRIMINATOR_ALIAS;
}
public String getDiscriminatorColumnReaderTemplate() {
return DISCRIMINATOR_ALIAS;
}
protected String getDiscriminatorAlias() {
return DISCRIMINATOR_ALIAS;
}
protected String getDiscriminatorFormulaTemplate() {
return null;
}
protected boolean isInverseTable(int j) {
return false;
}
protected boolean isNullableTable(int j) {
return false;
}
protected boolean isNullableSubclassTable(int j) {
return false;
}
protected boolean isInverseSubclassTable(int j) {
return false;
}
public boolean isSubclassEntityName(String entityName) {
return entityMetamodel.getSubclassEntityNames().contains(entityName);
}
private boolean[] getTableHasColumns() {
return tableHasColumns;
}
public String[] getRootTableKeyColumnNames() {
return rootTableKeyColumnNames;
}
protected String[] getSQLUpdateByRowIdStrings() {
if ( sqlUpdateByRowIdString == null ) {
throw new AssertionFailure( "no update by row id" );
}
String[] result = new String[getTableSpan() + 1];
result[0] = sqlUpdateByRowIdString;
System.arraycopy( sqlUpdateStrings, 0, result, 1, getTableSpan() );
return result;
}
protected String[] getSQLLazyUpdateByRowIdStrings() {
if ( sqlLazyUpdateByRowIdString == null ) {
throw new AssertionFailure( "no update by row id" );
}
String[] result = new String[getTableSpan()];
result[0] = sqlLazyUpdateByRowIdString;
for ( int i = 1; i < getTableSpan(); i++ ) {
result[i] = sqlLazyUpdateStrings[i];
}
return result;
}
protected String getSQLSnapshotSelectString() {
return sqlSnapshotSelectString;
}
protected String getSQLLazySelectString() {
return sqlLazySelectString;
}
protected String[] getSQLDeleteStrings() {
return sqlDeleteStrings;
}
protected String[] getSQLInsertStrings() {
return sqlInsertStrings;
}
protected String[] getSQLUpdateStrings() {
return sqlUpdateStrings;
}
protected String[] getSQLLazyUpdateStrings() {
return sqlLazyUpdateStrings;
}
/**
* The query that inserts a row, letting the database generate an id
*
* @return The IDENTITY-based insertion query.
*/
protected String getSQLIdentityInsertString() {
return sqlIdentityInsertString;
}
protected String getVersionSelectString() {
return sqlVersionSelectString;
}
protected boolean isInsertCallable(int j) {
return insertCallable[j];
}
protected boolean isUpdateCallable(int j) {
return updateCallable[j];
}
protected boolean isDeleteCallable(int j) {
return deleteCallable[j];
}
protected boolean isSubclassPropertyDeferred(String propertyName, String entityName) {
return false;
}
protected boolean isSubclassTableSequentialSelect(int j) {
return false;
}
public boolean hasSequentialSelect() {
return false;
}
/**
* Decide which tables need to be updated.
*
* The return here is an array of boolean values with each index corresponding
* to a given table in the scope of this persister.
*
* @param dirtyProperties The indices of all the entity properties considered dirty.
* @param hasDirtyCollection Whether any collections owned by the entity which were considered dirty.
*
* @return Array of booleans indicating which table require updating.
*/
protected boolean[] getTableUpdateNeeded(final int[] dirtyProperties, boolean hasDirtyCollection) {
if ( dirtyProperties == null ) {
return getTableHasColumns(); // for objects that came in via update()
}
else {
boolean[] updateability = getPropertyUpdateability();
int[] propertyTableNumbers = getPropertyTableNumbers();
boolean[] tableUpdateNeeded = new boolean[ getTableSpan() ];
for ( int i = 0; i < dirtyProperties.length; i++ ) {
int property = dirtyProperties[i];
int table = propertyTableNumbers[property];
tableUpdateNeeded[table] = tableUpdateNeeded[table] ||
( getPropertyColumnSpan(property) > 0 && updateability[property] );
}
if ( isVersioned() ) {
tableUpdateNeeded[0] = tableUpdateNeeded[0] ||
Versioning.isVersionIncrementRequired( dirtyProperties, hasDirtyCollection, getPropertyVersionability() );
}
return tableUpdateNeeded;
}
}
public boolean hasRowId() {
return rowIdName != null;
}
protected boolean[][] getPropertyColumnUpdateable() {
return propertyColumnUpdateable;
}
protected boolean[][] getPropertyColumnInsertable() {
return propertyColumnInsertable;
}
protected boolean[] getPropertySelectable() {
return propertySelectable;
}
public AbstractEntityPersister(
final PersistentClass persistentClass,
final EntityRegionAccessStrategy cacheAccessStrategy,
final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
final SessionFactoryImplementor factory) throws HibernateException {
// moved up from AbstractEntityPersister ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
this.factory = factory;
this.cacheAccessStrategy = cacheAccessStrategy;
this.naturalIdRegionAccessStrategy = naturalIdRegionAccessStrategy;
isLazyPropertiesCacheable = persistentClass.isLazyPropertiesCacheable();
this.entityMetamodel = new EntityMetamodel( persistentClass, this, factory );
this.entityTuplizer = this.entityMetamodel.getTuplizer();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int batch = persistentClass.getBatchSize();
if ( batch == -1 ) {
batch = factory.getSettings().getDefaultBatchFetchSize();
}
batchSize = batch;
hasSubselectLoadableCollections = persistentClass.hasSubselectLoadableCollections();
propertyMapping = new BasicEntityPropertyMapping( this );
// IDENTIFIER
identifierColumnSpan = persistentClass.getIdentifier().getColumnSpan();
rootTableKeyColumnNames = new String[identifierColumnSpan];
rootTableKeyColumnReaders = new String[identifierColumnSpan];
rootTableKeyColumnReaderTemplates = new String[identifierColumnSpan];
identifierAliases = new String[identifierColumnSpan];
rowIdName = persistentClass.getRootTable().getRowId();
loaderName = persistentClass.getLoaderName();
Iterator iter = persistentClass.getIdentifier().getColumnIterator();
int i = 0;
while ( iter.hasNext() ) {
Column col = ( Column ) iter.next();
rootTableKeyColumnNames[i] = col.getQuotedName( factory.getDialect() );
rootTableKeyColumnReaders[i] = col.getReadExpr( factory.getDialect() );
rootTableKeyColumnReaderTemplates[i] = col.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
identifierAliases[i] = col.getAlias( factory.getDialect(), persistentClass.getRootTable() );
i++;
}
// VERSION
if ( persistentClass.isVersioned() ) {
versionColumnName = ( ( Column ) persistentClass.getVersion().getColumnIterator().next() ).getQuotedName( factory.getDialect() );
}
else {
versionColumnName = null;
}
//WHERE STRING
sqlWhereString = StringHelper.isNotEmpty( persistentClass.getWhere() ) ? "( " + persistentClass.getWhere() + ") " : null;
sqlWhereStringTemplate = sqlWhereString == null ?
null :
Template.renderWhereStringTemplate( sqlWhereString, factory.getDialect(), factory.getSqlFunctionRegistry() );
// PROPERTIES
final boolean lazyAvailable = isInstrumented();
int hydrateSpan = entityMetamodel.getPropertySpan();
propertyColumnSpans = new int[hydrateSpan];
propertySubclassNames = new String[hydrateSpan];
propertyColumnAliases = new String[hydrateSpan][];
propertyColumnNames = new String[hydrateSpan][];
propertyColumnFormulaTemplates = new String[hydrateSpan][];
propertyColumnReaderTemplates = new String[hydrateSpan][];
propertyColumnWriters = new String[hydrateSpan][];
propertyUniqueness = new boolean[hydrateSpan];
propertySelectable = new boolean[hydrateSpan];
propertyColumnUpdateable = new boolean[hydrateSpan][];
propertyColumnInsertable = new boolean[hydrateSpan][];
HashSet thisClassProperties = new HashSet();
lazyProperties = new HashSet();
ArrayList lazyNames = new ArrayList();
ArrayList lazyNumbers = new ArrayList();
ArrayList lazyTypes = new ArrayList();
ArrayList lazyColAliases = new ArrayList();
iter = persistentClass.getPropertyClosureIterator();
i = 0;
boolean foundFormula = false;
while ( iter.hasNext() ) {
Property prop = ( Property ) iter.next();
thisClassProperties.add( prop );
int span = prop.getColumnSpan();
propertyColumnSpans[i] = span;
propertySubclassNames[i] = prop.getPersistentClass().getEntityName();
String[] colNames = new String[span];
String[] colAliases = new String[span];
String[] colReaderTemplates = new String[span];
String[] colWriters = new String[span];
String[] formulaTemplates = new String[span];
Iterator colIter = prop.getColumnIterator();
int k = 0;
while ( colIter.hasNext() ) {
Selectable thing = ( Selectable ) colIter.next();
colAliases[k] = thing.getAlias( factory.getDialect() , prop.getValue().getTable() );
if ( thing.isFormula() ) {
foundFormula = true;
formulaTemplates[k] = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
}
else {
Column col = (Column)thing;
colNames[k] = col.getQuotedName( factory.getDialect() );
colReaderTemplates[k] = col.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
colWriters[k] = col.getWriteExpr();
}
k++;
}
propertyColumnNames[i] = colNames;
propertyColumnFormulaTemplates[i] = formulaTemplates;
propertyColumnReaderTemplates[i] = colReaderTemplates;
propertyColumnWriters[i] = colWriters;
propertyColumnAliases[i] = colAliases;
if ( lazyAvailable && prop.isLazy() ) {
lazyProperties.add( prop.getName() );
lazyNames.add( prop.getName() );
lazyNumbers.add( i );
lazyTypes.add( prop.getValue().getType() );
lazyColAliases.add( colAliases );
}
propertyColumnUpdateable[i] = prop.getValue().getColumnUpdateability();
propertyColumnInsertable[i] = prop.getValue().getColumnInsertability();
propertySelectable[i] = prop.isSelectable();
propertyUniqueness[i] = prop.getValue().isAlternateUniqueKey();
if (prop.isLob() && getFactory().getDialect().forceLobAsLastValue() ) {
lobProperties.add( i );
}
i++;
}
hasFormulaProperties = foundFormula;
lazyPropertyColumnAliases = ArrayHelper.to2DStringArray( lazyColAliases );
lazyPropertyNames = ArrayHelper.toStringArray( lazyNames );
lazyPropertyNumbers = ArrayHelper.toIntArray( lazyNumbers );
lazyPropertyTypes = ArrayHelper.toTypeArray( lazyTypes );
// SUBCLASS PROPERTY CLOSURE
ArrayList columns = new ArrayList();
ArrayList columnsLazy = new ArrayList();
ArrayList columnReaderTemplates = new ArrayList();
ArrayList aliases = new ArrayList();
ArrayList formulas = new ArrayList();
ArrayList formulaAliases = new ArrayList();
ArrayList formulaTemplates = new ArrayList();
ArrayList formulasLazy = new ArrayList();
ArrayList types = new ArrayList();
ArrayList names = new ArrayList();
ArrayList classes = new ArrayList();
ArrayList templates = new ArrayList();
ArrayList propColumns = new ArrayList();
ArrayList propColumnReaders = new ArrayList();
ArrayList propColumnReaderTemplates = new ArrayList();
ArrayList joinedFetchesList = new ArrayList();
ArrayList cascades = new ArrayList();
ArrayList definedBySubclass = new ArrayList();
ArrayList propColumnNumbers = new ArrayList();
ArrayList propFormulaNumbers = new ArrayList();
ArrayList columnSelectables = new ArrayList();
ArrayList propNullables = new ArrayList();
iter = persistentClass.getSubclassPropertyClosureIterator();
while ( iter.hasNext() ) {
Property prop = ( Property ) iter.next();
names.add( prop.getName() );
classes.add( prop.getPersistentClass().getEntityName() );
boolean isDefinedBySubclass = !thisClassProperties.contains( prop );
definedBySubclass.add( Boolean.valueOf( isDefinedBySubclass ) );
propNullables.add( Boolean.valueOf( prop.isOptional() || isDefinedBySubclass ) ); //TODO: is this completely correct?
types.add( prop.getType() );
Iterator colIter = prop.getColumnIterator();
String[] cols = new String[prop.getColumnSpan()];
String[] readers = new String[prop.getColumnSpan()];
String[] readerTemplates = new String[prop.getColumnSpan()];
String[] forms = new String[prop.getColumnSpan()];
int[] colnos = new int[prop.getColumnSpan()];
int[] formnos = new int[prop.getColumnSpan()];
int l = 0;
Boolean lazy = Boolean.valueOf( prop.isLazy() && lazyAvailable );
while ( colIter.hasNext() ) {
Selectable thing = ( Selectable ) colIter.next();
if ( thing.isFormula() ) {
String template = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
formnos[l] = formulaTemplates.size();
colnos[l] = -1;
formulaTemplates.add( template );
forms[l] = template;
formulas.add( thing.getText( factory.getDialect() ) );
formulaAliases.add( thing.getAlias( factory.getDialect() ) );
formulasLazy.add( lazy );
}
else {
Column col = (Column)thing;
String colName = col.getQuotedName( factory.getDialect() );
colnos[l] = columns.size(); //before add :-)
formnos[l] = -1;
columns.add( colName );
cols[l] = colName;
aliases.add( thing.getAlias( factory.getDialect(), prop.getValue().getTable() ) );
columnsLazy.add( lazy );
columnSelectables.add( Boolean.valueOf( prop.isSelectable() ) );
readers[l] = col.getReadExpr( factory.getDialect() );
String readerTemplate = col.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
readerTemplates[l] = readerTemplate;
columnReaderTemplates.add( readerTemplate );
}
l++;
}
propColumns.add( cols );
propColumnReaders.add( readers );
propColumnReaderTemplates.add( readerTemplates );
templates.add( forms );
propColumnNumbers.add( colnos );
propFormulaNumbers.add( formnos );
joinedFetchesList.add( prop.getValue().getFetchMode() );
cascades.add( prop.getCascadeStyle() );
}
subclassColumnClosure = ArrayHelper.toStringArray( columns );
subclassColumnAliasClosure = ArrayHelper.toStringArray( aliases );
subclassColumnLazyClosure = ArrayHelper.toBooleanArray( columnsLazy );
subclassColumnSelectableClosure = ArrayHelper.toBooleanArray( columnSelectables );
subclassColumnReaderTemplateClosure = ArrayHelper.toStringArray( columnReaderTemplates );
subclassFormulaClosure = ArrayHelper.toStringArray( formulas );
subclassFormulaTemplateClosure = ArrayHelper.toStringArray( formulaTemplates );
subclassFormulaAliasClosure = ArrayHelper.toStringArray( formulaAliases );
subclassFormulaLazyClosure = ArrayHelper.toBooleanArray( formulasLazy );
subclassPropertyNameClosure = ArrayHelper.toStringArray( names );
subclassPropertySubclassNameClosure = ArrayHelper.toStringArray( classes );
subclassPropertyTypeClosure = ArrayHelper.toTypeArray( types );
subclassPropertyNullabilityClosure = ArrayHelper.toBooleanArray( propNullables );
subclassPropertyFormulaTemplateClosure = ArrayHelper.to2DStringArray( templates );
subclassPropertyColumnNameClosure = ArrayHelper.to2DStringArray( propColumns );
subclassPropertyColumnReaderClosure = ArrayHelper.to2DStringArray( propColumnReaders );
subclassPropertyColumnReaderTemplateClosure = ArrayHelper.to2DStringArray( propColumnReaderTemplates );
subclassPropertyColumnNumberClosure = ArrayHelper.to2DIntArray( propColumnNumbers );
subclassPropertyFormulaNumberClosure = ArrayHelper.to2DIntArray( propFormulaNumbers );
subclassPropertyCascadeStyleClosure = new CascadeStyle[cascades.size()];
iter = cascades.iterator();
int j = 0;
while ( iter.hasNext() ) {
subclassPropertyCascadeStyleClosure[j++] = ( CascadeStyle ) iter.next();
}
subclassPropertyFetchModeClosure = new FetchMode[joinedFetchesList.size()];
iter = joinedFetchesList.iterator();
j = 0;
while ( iter.hasNext() ) {
subclassPropertyFetchModeClosure[j++] = ( FetchMode ) iter.next();
}
propertyDefinedOnSubclass = new boolean[definedBySubclass.size()];
iter = definedBySubclass.iterator();
j = 0;
while ( iter.hasNext() ) {
propertyDefinedOnSubclass[j++] = (Boolean) iter.next();
}
// Handle any filters applied to the class level
filterHelper = new FilterHelper( persistentClass.getFilters(), factory );
temporaryIdTableName = persistentClass.getTemporaryIdTableName();
temporaryIdTableDDL = persistentClass.getTemporaryIdTableDDL();
this.cacheEntryHelper = buildCacheEntryHelper();
}
protected CacheEntryHelper buildCacheEntryHelper() {
if ( cacheAccessStrategy == null ) {
// the entity defined no caching...
return NoopCacheEntryHelper.INSTANCE;
}
if ( canUseReferenceCacheEntries() ) {
entityMetamodel.setLazy( false );
// todo : do we also need to unset proxy factory?
return new ReferenceCacheEntryHelper( this );
}
return factory.getSettings().isStructuredCacheEntriesEnabled()
? new StructuredCacheEntryHelper( this )
: new StandardCacheEntryHelper( this );
}
public boolean canUseReferenceCacheEntries() {
// todo : should really validate that the cache access type is read-only
if ( ! factory.getSettings().isDirectReferenceCacheEntriesEnabled() ) {
return false;
}
// for now, limit this to just entities that:
// 1) are immutable
if ( entityMetamodel.isMutable() ) {
return false;
}
// 2) have no associations. Eventually we want to be a little more lenient with associations.
for ( Type type : getSubclassPropertyTypeClosure() ) {
if ( type.isAssociationType() ) {
return false;
}
}
return true;
}
public AbstractEntityPersister(
final EntityBinding entityBinding,
final EntityRegionAccessStrategy cacheAccessStrategy,
final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
final SessionFactoryImplementor factory) throws HibernateException {
this.factory = factory;
this.cacheAccessStrategy = cacheAccessStrategy;
this.naturalIdRegionAccessStrategy = naturalIdRegionAccessStrategy;
this.isLazyPropertiesCacheable =
entityBinding.getHierarchyDetails().getCaching() == null ?
false :
entityBinding.getHierarchyDetails().getCaching().isCacheLazyProperties();
this.entityMetamodel = new EntityMetamodel( entityBinding, this, factory );
this.entityTuplizer = this.entityMetamodel.getTuplizer();
int batch = entityBinding.getBatchSize();
if ( batch == -1 ) {
batch = factory.getSettings().getDefaultBatchFetchSize();
}
batchSize = batch;
hasSubselectLoadableCollections = entityBinding.hasSubselectLoadableCollections();
propertyMapping = new BasicEntityPropertyMapping( this );
// IDENTIFIER
identifierColumnSpan = entityBinding.getHierarchyDetails().getEntityIdentifier().getValueBinding().getSimpleValueSpan();
rootTableKeyColumnNames = new String[identifierColumnSpan];
rootTableKeyColumnReaders = new String[identifierColumnSpan];
rootTableKeyColumnReaderTemplates = new String[identifierColumnSpan];
identifierAliases = new String[identifierColumnSpan];
rowIdName = entityBinding.getRowId();
loaderName = entityBinding.getCustomLoaderName();
int i = 0;
for ( org.hibernate.metamodel.relational.Column col : entityBinding.getPrimaryTable().getPrimaryKey().getColumns() ) {
rootTableKeyColumnNames[i] = col.getColumnName().encloseInQuotesIfQuoted( factory.getDialect() );
if ( col.getReadFragment() == null ) {
rootTableKeyColumnReaders[i] = rootTableKeyColumnNames[i];
rootTableKeyColumnReaderTemplates[i] = getTemplateFromColumn( col, factory );
}
else {
rootTableKeyColumnReaders[i] = col.getReadFragment();
rootTableKeyColumnReaderTemplates[i] = getTemplateFromString( rootTableKeyColumnReaders[i], factory );
}
identifierAliases[i] = col.getAlias( factory.getDialect() );
i++;
}
// VERSION
if ( entityBinding.isVersioned() ) {
final Value versioningValue = entityBinding.getHierarchyDetails().getVersioningAttributeBinding().getValue();
if ( ! org.hibernate.metamodel.relational.Column.class.isInstance( versioningValue ) ) {
throw new AssertionFailure( "Bad versioning attribute binding : " + versioningValue );
}
org.hibernate.metamodel.relational.Column versionColumn = org.hibernate.metamodel.relational.Column.class.cast( versioningValue );
versionColumnName = versionColumn.getColumnName().encloseInQuotesIfQuoted( factory.getDialect() );
}
else {
versionColumnName = null;
}
//WHERE STRING
sqlWhereString = StringHelper.isNotEmpty( entityBinding.getWhereFilter() ) ? "( " + entityBinding.getWhereFilter() + ") " : null;
sqlWhereStringTemplate = getTemplateFromString( sqlWhereString, factory );
// PROPERTIES
final boolean lazyAvailable = isInstrumented();
int hydrateSpan = entityMetamodel.getPropertySpan();
propertyColumnSpans = new int[hydrateSpan];
propertySubclassNames = new String[hydrateSpan];
propertyColumnAliases = new String[hydrateSpan][];
propertyColumnNames = new String[hydrateSpan][];
propertyColumnFormulaTemplates = new String[hydrateSpan][];
propertyColumnReaderTemplates = new String[hydrateSpan][];
propertyColumnWriters = new String[hydrateSpan][];
propertyUniqueness = new boolean[hydrateSpan];
propertySelectable = new boolean[hydrateSpan];
propertyColumnUpdateable = new boolean[hydrateSpan][];
propertyColumnInsertable = new boolean[hydrateSpan][];
HashSet thisClassProperties = new HashSet();
lazyProperties = new HashSet();
ArrayList lazyNames = new ArrayList();
ArrayList lazyNumbers = new ArrayList();
ArrayList lazyTypes = new ArrayList();
ArrayList lazyColAliases = new ArrayList();
i = 0;
boolean foundFormula = false;
for ( AttributeBinding attributeBinding : entityBinding.getAttributeBindingClosure() ) {
if ( attributeBinding == entityBinding.getHierarchyDetails().getEntityIdentifier().getValueBinding() ) {
// entity identifier is not considered a "normal" property
continue;
}
if ( ! attributeBinding.getAttribute().isSingular() ) {
// collections handled separately
continue;
}
final SingularAttributeBinding singularAttributeBinding = (SingularAttributeBinding) attributeBinding;
thisClassProperties.add( singularAttributeBinding );
propertySubclassNames[i] = ( (EntityBinding) singularAttributeBinding.getContainer() ).getEntity().getName();
int span = singularAttributeBinding.getSimpleValueSpan();
propertyColumnSpans[i] = span;
String[] colNames = new String[span];
String[] colAliases = new String[span];
String[] colReaderTemplates = new String[span];
String[] colWriters = new String[span];
String[] formulaTemplates = new String[span];
boolean[] propertyColumnInsertability = new boolean[span];
boolean[] propertyColumnUpdatability = new boolean[span];
int k = 0;
for ( SimpleValueBinding valueBinding : singularAttributeBinding.getSimpleValueBindings() ) {
colAliases[k] = valueBinding.getSimpleValue().getAlias( factory.getDialect() );
if ( valueBinding.isDerived() ) {
foundFormula = true;
formulaTemplates[ k ] = getTemplateFromString( ( (DerivedValue) valueBinding.getSimpleValue() ).getExpression(), factory );
}
else {
org.hibernate.metamodel.relational.Column col = ( org.hibernate.metamodel.relational.Column ) valueBinding.getSimpleValue();
colNames[k] = col.getColumnName().encloseInQuotesIfQuoted( factory.getDialect() );
colReaderTemplates[k] = getTemplateFromColumn( col, factory );
colWriters[k] = col.getWriteFragment() == null ? "?" : col.getWriteFragment();
}
propertyColumnInsertability[k] = valueBinding.isIncludeInInsert();
propertyColumnUpdatability[k] = valueBinding.isIncludeInUpdate();
k++;
}
propertyColumnNames[i] = colNames;
propertyColumnFormulaTemplates[i] = formulaTemplates;
propertyColumnReaderTemplates[i] = colReaderTemplates;
propertyColumnWriters[i] = colWriters;
propertyColumnAliases[i] = colAliases;
propertyColumnUpdateable[i] = propertyColumnUpdatability;
propertyColumnInsertable[i] = propertyColumnInsertability;
if ( lazyAvailable && singularAttributeBinding.isLazy() ) {
lazyProperties.add( singularAttributeBinding.getAttribute().getName() );
lazyNames.add( singularAttributeBinding.getAttribute().getName() );
lazyNumbers.add( i );
lazyTypes.add( singularAttributeBinding.getHibernateTypeDescriptor().getResolvedTypeMapping());
lazyColAliases.add( colAliases );
}
// TODO: fix this when backrefs are working
//propertySelectable[i] = singularAttributeBinding.isBackRef();
propertySelectable[i] = true;
propertyUniqueness[i] = singularAttributeBinding.isAlternateUniqueKey();
// TODO: Does this need AttributeBindings wired into lobProperties? Currently in Property only.
i++;
}
hasFormulaProperties = foundFormula;
lazyPropertyColumnAliases = ArrayHelper.to2DStringArray( lazyColAliases );
lazyPropertyNames = ArrayHelper.toStringArray( lazyNames );
lazyPropertyNumbers = ArrayHelper.toIntArray( lazyNumbers );
lazyPropertyTypes = ArrayHelper.toTypeArray( lazyTypes );
// SUBCLASS PROPERTY CLOSURE
List columns = new ArrayList();
List columnsLazy = new ArrayList();
List columnReaderTemplates = new ArrayList();
List aliases = new ArrayList();
List formulas = new ArrayList();
List formulaAliases = new ArrayList();
List formulaTemplates = new ArrayList();
List formulasLazy = new ArrayList();
List types = new ArrayList();
List names = new ArrayList();
List classes = new ArrayList();
List templates = new ArrayList();
List propColumns = new ArrayList();
List propColumnReaders = new ArrayList();
List propColumnReaderTemplates = new ArrayList();
List joinedFetchesList = new ArrayList();
List cascades = new ArrayList();
List definedBySubclass = new ArrayList();
List propColumnNumbers = new ArrayList();
List propFormulaNumbers = new ArrayList();
List columnSelectables = new ArrayList();
List propNullables = new ArrayList();
for ( AttributeBinding attributeBinding : entityBinding.getSubEntityAttributeBindingClosure() ) {
if ( attributeBinding == entityBinding.getHierarchyDetails().getEntityIdentifier().getValueBinding() ) {
// entity identifier is not considered a "normal" property
continue;
}
if ( ! attributeBinding.getAttribute().isSingular() ) {
// collections handled separately
continue;
}
final SingularAttributeBinding singularAttributeBinding = (SingularAttributeBinding) attributeBinding;
names.add( singularAttributeBinding.getAttribute().getName() );
classes.add( ( (EntityBinding) singularAttributeBinding.getContainer() ).getEntity().getName() );
boolean isDefinedBySubclass = ! thisClassProperties.contains( singularAttributeBinding );
definedBySubclass.add( isDefinedBySubclass );
propNullables.add( singularAttributeBinding.isNullable() || isDefinedBySubclass ); //TODO: is this completely correct?
types.add( singularAttributeBinding.getHibernateTypeDescriptor().getResolvedTypeMapping() );
final int span = singularAttributeBinding.getSimpleValueSpan();
String[] cols = new String[ span ];
String[] readers = new String[ span ];
String[] readerTemplates = new String[ span ];
String[] forms = new String[ span ];
int[] colnos = new int[ span ];
int[] formnos = new int[ span ];
int l = 0;
Boolean lazy = singularAttributeBinding.isLazy() && lazyAvailable;
for ( SimpleValueBinding valueBinding : singularAttributeBinding.getSimpleValueBindings() ) {
if ( valueBinding.isDerived() ) {
DerivedValue derivedValue = DerivedValue.class.cast( valueBinding.getSimpleValue() );
String template = getTemplateFromString( derivedValue.getExpression(), factory );
formnos[l] = formulaTemplates.size();
colnos[l] = -1;
formulaTemplates.add( template );
forms[l] = template;
formulas.add( derivedValue.getExpression() );
formulaAliases.add( derivedValue.getAlias( factory.getDialect() ) );
formulasLazy.add( lazy );
}
else {
org.hibernate.metamodel.relational.Column col = org.hibernate.metamodel.relational.Column.class.cast( valueBinding.getSimpleValue() );
String colName = col.getColumnName().encloseInQuotesIfQuoted( factory.getDialect() );
colnos[l] = columns.size(); //before add :-)
formnos[l] = -1;
columns.add( colName );
cols[l] = colName;
aliases.add( col.getAlias( factory.getDialect() ) );
columnsLazy.add( lazy );
// TODO: properties only selectable if they are non-plural???
columnSelectables.add( singularAttributeBinding.getAttribute().isSingular() );
readers[l] =
col.getReadFragment() == null ?
col.getColumnName().encloseInQuotesIfQuoted( factory.getDialect() ) :
col.getReadFragment();
String readerTemplate = getTemplateFromColumn( col, factory );
readerTemplates[l] = readerTemplate;
columnReaderTemplates.add( readerTemplate );
}
l++;
}
propColumns.add( cols );
propColumnReaders.add( readers );
propColumnReaderTemplates.add( readerTemplates );
templates.add( forms );
propColumnNumbers.add( colnos );
propFormulaNumbers.add( formnos );
if ( singularAttributeBinding.isAssociation() ) {
AssociationAttributeBinding associationAttributeBinding =
( AssociationAttributeBinding ) singularAttributeBinding;
cascades.add( associationAttributeBinding.getCascadeStyle() );
joinedFetchesList.add( associationAttributeBinding.getFetchMode() );
}
else {
cascades.add( CascadeStyles.NONE );
joinedFetchesList.add( FetchMode.SELECT );
}
}
subclassColumnClosure = ArrayHelper.toStringArray( columns );
subclassColumnAliasClosure = ArrayHelper.toStringArray( aliases );
subclassColumnLazyClosure = ArrayHelper.toBooleanArray( columnsLazy );
subclassColumnSelectableClosure = ArrayHelper.toBooleanArray( columnSelectables );
subclassColumnReaderTemplateClosure = ArrayHelper.toStringArray( columnReaderTemplates );
subclassFormulaClosure = ArrayHelper.toStringArray( formulas );
subclassFormulaTemplateClosure = ArrayHelper.toStringArray( formulaTemplates );
subclassFormulaAliasClosure = ArrayHelper.toStringArray( formulaAliases );
subclassFormulaLazyClosure = ArrayHelper.toBooleanArray( formulasLazy );
subclassPropertyNameClosure = ArrayHelper.toStringArray( names );
subclassPropertySubclassNameClosure = ArrayHelper.toStringArray( classes );
subclassPropertyTypeClosure = ArrayHelper.toTypeArray( types );
subclassPropertyNullabilityClosure = ArrayHelper.toBooleanArray( propNullables );
subclassPropertyFormulaTemplateClosure = ArrayHelper.to2DStringArray( templates );
subclassPropertyColumnNameClosure = ArrayHelper.to2DStringArray( propColumns );
subclassPropertyColumnReaderClosure = ArrayHelper.to2DStringArray( propColumnReaders );
subclassPropertyColumnReaderTemplateClosure = ArrayHelper.to2DStringArray( propColumnReaderTemplates );
subclassPropertyColumnNumberClosure = ArrayHelper.to2DIntArray( propColumnNumbers );
subclassPropertyFormulaNumberClosure = ArrayHelper.to2DIntArray( propFormulaNumbers );
subclassPropertyCascadeStyleClosure = cascades.toArray( new CascadeStyle[ cascades.size() ] );
subclassPropertyFetchModeClosure = joinedFetchesList.toArray( new FetchMode[ joinedFetchesList.size() ] );
propertyDefinedOnSubclass = ArrayHelper.toBooleanArray( definedBySubclass );
List filterDefaultConditions = new ArrayList();
for ( FilterDefinition filterDefinition : entityBinding.getFilterDefinitions() ) {
filterDefaultConditions.add(new FilterConfiguration(filterDefinition.getFilterName(),
filterDefinition.getDefaultFilterCondition(), true, null, null, null));
}
filterHelper = new FilterHelper( filterDefaultConditions, factory);
temporaryIdTableName = null;
temporaryIdTableDDL = null;
this.cacheEntryHelper = buildCacheEntryHelper();
}
protected static String getTemplateFromString(String string, SessionFactoryImplementor factory) {
return string == null ?
null :
Template.renderWhereStringTemplate( string, factory.getDialect(), factory.getSqlFunctionRegistry() );
}
public String getTemplateFromColumn(org.hibernate.metamodel.relational.Column column, SessionFactoryImplementor factory) {
String templateString;
if ( column.getReadFragment() != null ) {
templateString = getTemplateFromString( column.getReadFragment(), factory );
}
else {
String columnName = column.getColumnName().encloseInQuotesIfQuoted( factory.getDialect() );
templateString = Template.TEMPLATE + '.' + columnName;
}
return templateString;
}
protected String generateLazySelectString() {
if ( !entityMetamodel.hasLazyProperties() ) {
return null;
}
HashSet tableNumbers = new HashSet();
ArrayList columnNumbers = new ArrayList();
ArrayList formulaNumbers = new ArrayList();
for ( int i = 0; i < lazyPropertyNames.length; i++ ) {
// all this only really needs to consider properties
// of this class, not its subclasses, but since we
// are reusing code used for sequential selects, we
// use the subclass closure
int propertyNumber = getSubclassPropertyIndex( lazyPropertyNames[i] );
int tableNumber = getSubclassPropertyTableNumber( propertyNumber );
tableNumbers.add( tableNumber );
int[] colNumbers = subclassPropertyColumnNumberClosure[propertyNumber];
for ( int j = 0; j < colNumbers.length; j++ ) {
if ( colNumbers[j]!=-1 ) {
columnNumbers.add( colNumbers[j] );
}
}
int[] formNumbers = subclassPropertyFormulaNumberClosure[propertyNumber];
for ( int j = 0; j < formNumbers.length; j++ ) {
if ( formNumbers[j]!=-1 ) {
formulaNumbers.add( formNumbers[j] );
}
}
}
if ( columnNumbers.size()==0 && formulaNumbers.size()==0 ) {
// only one-to-one is lazy fetched
return null;
}
return renderSelect( ArrayHelper.toIntArray( tableNumbers ),
ArrayHelper.toIntArray( columnNumbers ),
ArrayHelper.toIntArray( formulaNumbers ) );
}
public Object initializeLazyProperty(String fieldName, Object entity, SessionImplementor session)
throws HibernateException {
final Serializable id = session.getContextEntityIdentifier( entity );
final EntityEntry entry = session.getPersistenceContext().getEntry( entity );
if ( entry == null ) {
throw new HibernateException( "entity is not associated with the session: " + id );
}
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Initializing lazy properties of: {0}, field access: {1}", MessageHelper.infoString( this, id, getFactory() ), fieldName );
}
if ( session.getCacheMode().isGetEnabled() && hasCache() ) {
final CacheKey cacheKey = session.generateCacheKey( id, getIdentifierType(), getEntityName() );
final Object ce = CacheHelper.fromSharedCache( session, cacheKey, getCacheAccessStrategy() );
if ( ce != null ) {
final CacheEntry cacheEntry = (CacheEntry) getCacheEntryStructure().destructure(ce, factory);
if ( !cacheEntry.areLazyPropertiesUnfetched() ) {
//note early exit here:
return initializeLazyPropertiesFromCache( fieldName, entity, session, entry, cacheEntry );
}
}
}
return initializeLazyPropertiesFromDatastore( fieldName, entity, session, id, entry );
}
private Object initializeLazyPropertiesFromDatastore(
final String fieldName,
final Object entity,
final SessionImplementor session,
final Serializable id,
final EntityEntry entry) {
if ( !hasLazyProperties() ) throw new AssertionFailure( "no lazy properties" );
LOG.trace( "Initializing lazy properties from datastore" );
try {
Object result = null;
PreparedStatement ps = null;
try {
final String lazySelect = getSQLLazySelectString();
ResultSet rs = null;
try {
if ( lazySelect != null ) {
// null sql means that the only lazy properties
// are shared PK one-to-one associations which are
// handled differently in the Type#nullSafeGet code...
ps = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( lazySelect );
getIdentifierType().nullSafeSet( ps, id, 1, session );
rs = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().extract( ps );
rs.next();
}
final Object[] snapshot = entry.getLoadedState();
for ( int j = 0; j < lazyPropertyNames.length; j++ ) {
Object propValue = lazyPropertyTypes[j].nullSafeGet( rs, lazyPropertyColumnAliases[j], session, entity );
if ( initializeLazyProperty( fieldName, entity, session, snapshot, j, propValue ) ) {
result = propValue;
}
}
}
finally {
if ( rs != null ) {
session.getTransactionCoordinator().getJdbcCoordinator().release( rs, ps );
}
}
}
finally {
if ( ps != null ) {
session.getTransactionCoordinator().getJdbcCoordinator().release( ps );
}
}
LOG.trace( "Done initializing lazy properties" );
return result;
}
catch ( SQLException sqle ) {
throw getFactory().getSQLExceptionHelper().convert(
sqle,
"could not initialize lazy properties: " +
MessageHelper.infoString( this, id, getFactory() ),
getSQLLazySelectString()
);
}
}
private Object initializeLazyPropertiesFromCache(
final String fieldName,
final Object entity,
final SessionImplementor session,
final EntityEntry entry,
final CacheEntry cacheEntry
) {
LOG.trace( "Initializing lazy properties from second-level cache" );
Object result = null;
Serializable[] disassembledValues = cacheEntry.getDisassembledState();
final Object[] snapshot = entry.getLoadedState();
for ( int j = 0; j < lazyPropertyNames.length; j++ ) {
final Object propValue = lazyPropertyTypes[j].assemble(
disassembledValues[ lazyPropertyNumbers[j] ],
session,
entity
);
if ( initializeLazyProperty( fieldName, entity, session, snapshot, j, propValue ) ) {
result = propValue;
}
}
LOG.trace( "Done initializing lazy properties" );
return result;
}
private boolean initializeLazyProperty(
final String fieldName,
final Object entity,
final SessionImplementor session,
final Object[] snapshot,
final int j,
final Object propValue) {
setPropertyValue( entity, lazyPropertyNumbers[j], propValue );
if ( snapshot != null ) {
// object have been loaded with setReadOnly(true); HHH-2236
snapshot[ lazyPropertyNumbers[j] ] = lazyPropertyTypes[j].deepCopy( propValue, factory );
}
return fieldName.equals( lazyPropertyNames[j] );
}
public boolean isBatchable() {
return optimisticLockStyle() == OptimisticLockStyle.NONE
|| ( !isVersioned() && optimisticLockStyle() == OptimisticLockStyle.VERSION )
|| getFactory().getSettings().isJdbcBatchVersionedData();
}
public Serializable[] getQuerySpaces() {
return getPropertySpaces();
}
protected Set getLazyProperties() {
return lazyProperties;
}
public boolean isBatchLoadable() {
return batchSize > 1;
}
public String[] getIdentifierColumnNames() {
return rootTableKeyColumnNames;
}
public String[] getIdentifierColumnReaders() {
return rootTableKeyColumnReaders;
}
public String[] getIdentifierColumnReaderTemplates() {
return rootTableKeyColumnReaderTemplates;
}
protected int getIdentifierColumnSpan() {
return identifierColumnSpan;
}
protected String[] getIdentifierAliases() {
return identifierAliases;
}
public String getVersionColumnName() {
return versionColumnName;
}
protected String getVersionedTableName() {
return getTableName( 0 );
}
protected boolean[] getSubclassColumnLazyiness() {
return subclassColumnLazyClosure;
}
protected boolean[] getSubclassFormulaLazyiness() {
return subclassFormulaLazyClosure;
}
/**
* We can't immediately add to the cache if we have formulas
* which must be evaluated, or if we have the possibility of
* two concurrent updates to the same item being merged on
* the database. This can happen if (a) the item is not
* versioned and either (b) we have dynamic update enabled
* or (c) we have multiple tables holding the state of the
* item.
*/
public boolean isCacheInvalidationRequired() {
return hasFormulaProperties() ||
( !isVersioned() && ( entityMetamodel.isDynamicUpdate() || getTableSpan() > 1 ) );
}
public boolean isLazyPropertiesCacheable() {
return isLazyPropertiesCacheable;
}
public String selectFragment(String alias, String suffix) {
return identifierSelectFragment( alias, suffix ) +
propertySelectFragment( alias, suffix, false );
}
public String[] getIdentifierAliases(String suffix) {
// NOTE: this assumes something about how propertySelectFragment is implemented by the subclass!
// was toUnqotedAliasStrings( getIdentiferColumnNames() ) before - now tried
// to remove that unqoting and missing aliases..
return new Alias( suffix ).toAliasStrings( getIdentifierAliases() );
}
public String[] getPropertyAliases(String suffix, int i) {
// NOTE: this assumes something about how propertySelectFragment is implemented by the subclass!
return new Alias( suffix ).toUnquotedAliasStrings( propertyColumnAliases[i] );
}
public String getDiscriminatorAlias(String suffix) {
// NOTE: this assumes something about how propertySelectFragment is implemented by the subclass!
// was toUnqotedAliasStrings( getdiscriminatorColumnName() ) before - now tried
// to remove that unqoting and missing aliases..
return entityMetamodel.hasSubclasses() ?
new Alias( suffix ).toAliasString( getDiscriminatorAlias() ) :
null;
}
public String identifierSelectFragment(String name, String suffix) {
return new SelectFragment()
.setSuffix( suffix )
.addColumns( name, getIdentifierColumnNames(), getIdentifierAliases() )
.toFragmentString()
.substring( 2 ); //strip leading ", "
}
public String propertySelectFragment(String tableAlias, String suffix, boolean allProperties) {
return propertySelectFragmentFragment( tableAlias, suffix, allProperties ).toFragmentString();
}
public SelectFragment propertySelectFragmentFragment(
String tableAlias,
String suffix,
boolean allProperties) {
SelectFragment select = new SelectFragment()
.setSuffix( suffix )
.setUsedAliases( getIdentifierAliases() );
int[] columnTableNumbers = getSubclassColumnTableNumberClosure();
String[] columnAliases = getSubclassColumnAliasClosure();
String[] columnReaderTemplates = getSubclassColumnReaderTemplateClosure();
for ( int i = 0; i < getSubclassColumnClosure().length; i++ ) {
boolean selectable = ( allProperties || !subclassColumnLazyClosure[i] ) &&
!isSubclassTableSequentialSelect( columnTableNumbers[i] ) &&
subclassColumnSelectableClosure[i];
if ( selectable ) {
String subalias = generateTableAlias( tableAlias, columnTableNumbers[i] );
select.addColumnTemplate( subalias, columnReaderTemplates[i], columnAliases[i] );
}
}
int[] formulaTableNumbers = getSubclassFormulaTableNumberClosure();
String[] formulaTemplates = getSubclassFormulaTemplateClosure();
String[] formulaAliases = getSubclassFormulaAliasClosure();
for ( int i = 0; i < getSubclassFormulaTemplateClosure().length; i++ ) {
boolean selectable = ( allProperties || !subclassFormulaLazyClosure[i] )
&& !isSubclassTableSequentialSelect( formulaTableNumbers[i] );
if ( selectable ) {
String subalias = generateTableAlias( tableAlias, formulaTableNumbers[i] );
select.addFormula( subalias, formulaTemplates[i], formulaAliases[i] );
}
}
if ( entityMetamodel.hasSubclasses() ) {
addDiscriminatorToSelect( select, tableAlias, suffix );
}
if ( hasRowId() ) {
select.addColumn( tableAlias, rowIdName, ROWID_ALIAS );
}
return select;
}
public Object[] getDatabaseSnapshot(Serializable id, SessionImplementor session)
throws HibernateException {
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Getting current persistent state for: {0}", MessageHelper.infoString( this, id, getFactory() ) );
}
try {
PreparedStatement ps = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( getSQLSnapshotSelectString() );
try {
getIdentifierType().nullSafeSet( ps, id, 1, session );
//if ( isVersioned() ) getVersionType().nullSafeSet( ps, version, getIdentifierColumnSpan()+1, session );
ResultSet rs = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().extract( ps );
try {
//if there is no resulting row, return null
if ( !rs.next() ) {
return null;
}
//otherwise return the "hydrated" state (ie. associations are not resolved)
Type[] types = getPropertyTypes();
Object[] values = new Object[types.length];
boolean[] includeProperty = getPropertyUpdateability();
for ( int i = 0; i < types.length; i++ ) {
if ( includeProperty[i] ) {
values[i] = types[i].hydrate( rs, getPropertyAliases( "", i ), session, null ); //null owner ok??
}
}
return values;
}
finally {
session.getTransactionCoordinator().getJdbcCoordinator().release( rs, ps );
}
}
finally {
session.getTransactionCoordinator().getJdbcCoordinator().release( ps );
}
}
catch ( SQLException e ) {
throw getFactory().getSQLExceptionHelper().convert(
e,
"could not retrieve snapshot: " + MessageHelper.infoString( this, id, getFactory() ),
getSQLSnapshotSelectString()
);
}
}
@Override
public Serializable getIdByUniqueKey(Serializable key, String uniquePropertyName, SessionImplementor session) throws HibernateException {
if ( LOG.isTraceEnabled() ) {
LOG.tracef(
"resolving unique key [%s] to identifier for entity [%s]",
key,
getEntityName()
);
}
int propertyIndex = getSubclassPropertyIndex( uniquePropertyName );
if ( propertyIndex < 0 ) {
throw new HibernateException(
"Could not determine Type for property [" + uniquePropertyName + "] on entity [" + getEntityName() + "]"
);
}
Type propertyType = getSubclassPropertyType( propertyIndex );
try {
PreparedStatement ps = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( generateIdByUniqueKeySelectString( uniquePropertyName ) );
try {
propertyType.nullSafeSet( ps, key, 1, session );
ResultSet rs = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().extract( ps );
try {
//if there is no resulting row, return null
if ( !rs.next() ) {
return null;
}
return (Serializable) getIdentifierType().nullSafeGet( rs, getIdentifierAliases(), session, null );
}
finally {
session.getTransactionCoordinator().getJdbcCoordinator().release( rs, ps );
}
}
finally {
session.getTransactionCoordinator().getJdbcCoordinator().release( ps );
}
}
catch ( SQLException e ) {
throw getFactory().getSQLExceptionHelper().convert(
e,
String.format(
"could not resolve unique property [%s] to identifier for entity [%s]",
uniquePropertyName,
getEntityName()
),
getSQLSnapshotSelectString()
);
}
}
protected String generateIdByUniqueKeySelectString(String uniquePropertyName) {
Select select = new Select( getFactory().getDialect() );
if ( getFactory().getSettings().isCommentsEnabled() ) {
select.setComment( "resolve id by unique property [" + getEntityName() + "." + uniquePropertyName + "]" );
}
final String rooAlias = getRootAlias();
select.setFromClause( fromTableFragment( rooAlias ) + fromJoinFragment( rooAlias, true, false ) );
SelectFragment selectFragment = new SelectFragment();
selectFragment.addColumns( rooAlias, getIdentifierColumnNames(), getIdentifierAliases() );
select.setSelectClause( selectFragment );
StringBuilder whereClauseBuffer = new StringBuilder();
final int uniquePropertyIndex = getSubclassPropertyIndex( uniquePropertyName );
final String uniquePropertyTableAlias = generateTableAlias(
rooAlias,
getSubclassPropertyTableNumber( uniquePropertyIndex )
);
String sep = "";
for ( String columnTemplate : getSubclassPropertyColumnReaderTemplateClosure()[uniquePropertyIndex] ) {
if ( columnTemplate == null ) {
continue;
}
final String columnReference = StringHelper.replace( columnTemplate, Template.TEMPLATE, uniquePropertyTableAlias );
whereClauseBuffer.append( sep ).append( columnReference ).append( "=?" );
sep = " and ";
}
for ( String formulaTemplate : getSubclassPropertyFormulaTemplateClosure()[uniquePropertyIndex] ) {
if ( formulaTemplate == null ) {
continue;
}
final String formulaReference = StringHelper.replace( formulaTemplate, Template.TEMPLATE, uniquePropertyTableAlias );
whereClauseBuffer.append( sep ).append( formulaReference ).append( "=?" );
sep = " and ";
}
whereClauseBuffer.append( whereJoinFragment( rooAlias, true, false ) );
select.setWhereClause( whereClauseBuffer.toString() );
return select.setOuterJoins( "", "" ).toStatementString();
}
/**
* Generate the SQL that selects the version number by id
*/
protected String generateSelectVersionString() {
SimpleSelect select = new SimpleSelect( getFactory().getDialect() )
.setTableName( getVersionedTableName() );
if ( isVersioned() ) {
select.addColumn( versionColumnName );
}
else {
select.addColumns( rootTableKeyColumnNames );
}
if ( getFactory().getSettings().isCommentsEnabled() ) {
select.setComment( "get version " + getEntityName() );
}
return select.addCondition( rootTableKeyColumnNames, "=?" ).toStatementString();
}
public boolean[] getPropertyUniqueness() {
return propertyUniqueness;
}
protected String generateInsertGeneratedValuesSelectString() {
return generateGeneratedValuesSelectString( GenerationTiming.INSERT );
}
protected String generateUpdateGeneratedValuesSelectString() {
return generateGeneratedValuesSelectString( GenerationTiming.ALWAYS );
}
private String generateGeneratedValuesSelectString(final GenerationTiming generationTimingToMatch) {
Select select = new Select( getFactory().getDialect() );
if ( getFactory().getSettings().isCommentsEnabled() ) {
select.setComment( "get generated state " + getEntityName() );
}
String[] aliasedIdColumns = StringHelper.qualify( getRootAlias(), getIdentifierColumnNames() );
// Here we render the select column list based on the properties defined as being generated.
// For partial component generation, we currently just re-select the whole component
// rather than trying to handle the individual generated portions.
String selectClause = concretePropertySelectFragment(
getRootAlias(),
new InclusionChecker() {
@Override
public boolean includeProperty(int propertyNumber) {
final InDatabaseValueGenerationStrategy generationStrategy
= entityMetamodel.getInDatabaseValueGenerationStrategies()[propertyNumber];
return generationStrategy != null
&& timingsMatch( generationStrategy.getGenerationTiming(), generationTimingToMatch );
}
}
);
selectClause = selectClause.substring( 2 );
String fromClause = fromTableFragment( getRootAlias() ) +
fromJoinFragment( getRootAlias(), true, false );
String whereClause = new StringBuilder()
.append( StringHelper.join( "=? and ", aliasedIdColumns ) )
.append( "=?" )
.append( whereJoinFragment( getRootAlias(), true, false ) )
.toString();
return select.setSelectClause( selectClause )
.setFromClause( fromClause )
.setOuterJoins( "", "" )
.setWhereClause( whereClause )
.toStatementString();
}
protected static interface InclusionChecker {
public boolean includeProperty(int propertyNumber);
}
protected String concretePropertySelectFragment(String alias, final boolean[] includeProperty) {
return concretePropertySelectFragment(
alias,
new InclusionChecker() {
public boolean includeProperty(int propertyNumber) {
return includeProperty[propertyNumber];
}
}
);
}
protected String concretePropertySelectFragment(String alias, InclusionChecker inclusionChecker) {
int propertyCount = getPropertyNames().length;
int[] propertyTableNumbers = getPropertyTableNumbersInSelect();
SelectFragment frag = new SelectFragment();
for ( int i = 0; i < propertyCount; i++ ) {
if ( inclusionChecker.includeProperty( i ) ) {
frag.addColumnTemplates(
generateTableAlias( alias, propertyTableNumbers[i] ),
propertyColumnReaderTemplates[i],
propertyColumnAliases[i]
);
frag.addFormulas(
generateTableAlias( alias, propertyTableNumbers[i] ),
propertyColumnFormulaTemplates[i],
propertyColumnAliases[i]
);
}
}
return frag.toFragmentString();
}
protected String generateSnapshotSelectString() {
//TODO: should we use SELECT .. FOR UPDATE?
Select select = new Select( getFactory().getDialect() );
if ( getFactory().getSettings().isCommentsEnabled() ) {
select.setComment( "get current state " + getEntityName() );
}
String[] aliasedIdColumns = StringHelper.qualify( getRootAlias(), getIdentifierColumnNames() );
String selectClause = StringHelper.join( ", ", aliasedIdColumns ) +
concretePropertySelectFragment( getRootAlias(), getPropertyUpdateability() );
String fromClause = fromTableFragment( getRootAlias() ) +
fromJoinFragment( getRootAlias(), true, false );
String whereClause = new StringBuilder()
.append( StringHelper.join( "=? and ",
aliasedIdColumns ) )
.append( "=?" )
.append( whereJoinFragment( getRootAlias(), true, false ) )
.toString();
/*if ( isVersioned() ) {
where.append(" and ")
.append( getVersionColumnName() )
.append("=?");
}*/
return select.setSelectClause( selectClause )
.setFromClause( fromClause )
.setOuterJoins( "", "" )
.setWhereClause( whereClause )
.toStatementString();
}
public Object forceVersionIncrement(Serializable id, Object currentVersion, SessionImplementor session) {
if ( !isVersioned() ) {
throw new AssertionFailure( "cannot force version increment on non-versioned entity" );
}
if ( isVersionPropertyGenerated() ) {
// the difficulty here is exactly what do we update in order to
// force the version to be incremented in the db...
throw new HibernateException( "LockMode.FORCE is currently not supported for generated version properties" );
}
Object nextVersion = getVersionType().next( currentVersion, session );
if (LOG.isTraceEnabled()) LOG.trace("Forcing version increment [" + MessageHelper.infoString(this, id, getFactory()) + "; "
+ getVersionType().toLoggableString(currentVersion, getFactory()) + " -> "
+ getVersionType().toLoggableString(nextVersion, getFactory()) + "]");
// todo : cache this sql...
String versionIncrementString = generateVersionIncrementUpdateString();
PreparedStatement st = null;
try {
st = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( versionIncrementString, false );
try {
getVersionType().nullSafeSet( st, nextVersion, 1, session );
getIdentifierType().nullSafeSet( st, id, 2, session );
getVersionType().nullSafeSet( st, currentVersion, 2 + getIdentifierColumnSpan(), session );
int rows = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().executeUpdate( st );
if ( rows != 1 ) {
throw new StaleObjectStateException( getEntityName(), id );
}
}
finally {
session.getTransactionCoordinator().getJdbcCoordinator().release( st );
}
}
catch ( SQLException sqle ) {
throw getFactory().getSQLExceptionHelper().convert(
sqle,
"could not retrieve version: " +
MessageHelper.infoString( this, id, getFactory() ),
getVersionSelectString()
);
}
return nextVersion;
}
private String generateVersionIncrementUpdateString() {
Update update = new Update( getFactory().getDialect() );
update.setTableName( getTableName( 0 ) );
if ( getFactory().getSettings().isCommentsEnabled() ) {
update.setComment( "forced version increment" );
}
update.addColumn( getVersionColumnName() );
update.addPrimaryKeyColumns( getIdentifierColumnNames() );
update.setVersionColumnName( getVersionColumnName() );
return update.toStatementString();
}
/**
* Retrieve the version number
*/
public Object getCurrentVersion(Serializable id, SessionImplementor session) throws HibernateException {
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Getting version: {0}", MessageHelper.infoString( this, id, getFactory() ) );
}
try {
PreparedStatement st = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( getVersionSelectString() );
try {
getIdentifierType().nullSafeSet( st, id, 1, session );
ResultSet rs = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().extract( st );
try {
if ( !rs.next() ) {
return null;
}
if ( !isVersioned() ) {
return this;
}
return getVersionType().nullSafeGet( rs, getVersionColumnName(), session, null );
}
finally {
session.getTransactionCoordinator().getJdbcCoordinator().release( rs, st );
}
}
finally {
session.getTransactionCoordinator().getJdbcCoordinator().release( st );
}
}
catch ( SQLException e ) {
throw getFactory().getSQLExceptionHelper().convert(
e,
"could not retrieve version: " + MessageHelper.infoString( this, id, getFactory() ),
getVersionSelectString()
);
}
}
protected void initLockers() {
lockers.put( LockMode.READ, generateLocker( LockMode.READ ) );
lockers.put( LockMode.UPGRADE, generateLocker( LockMode.UPGRADE ) );
lockers.put( LockMode.UPGRADE_NOWAIT, generateLocker( LockMode.UPGRADE_NOWAIT ) );
lockers.put( LockMode.UPGRADE_SKIPLOCKED, generateLocker( LockMode.UPGRADE_SKIPLOCKED ) );
lockers.put( LockMode.FORCE, generateLocker( LockMode.FORCE ) );
lockers.put( LockMode.PESSIMISTIC_READ, generateLocker( LockMode.PESSIMISTIC_READ ) );
lockers.put( LockMode.PESSIMISTIC_WRITE, generateLocker( LockMode.PESSIMISTIC_WRITE ) );
lockers.put( LockMode.PESSIMISTIC_FORCE_INCREMENT, generateLocker( LockMode.PESSIMISTIC_FORCE_INCREMENT ) );
lockers.put( LockMode.OPTIMISTIC, generateLocker( LockMode.OPTIMISTIC ) );
lockers.put( LockMode.OPTIMISTIC_FORCE_INCREMENT, generateLocker( LockMode.OPTIMISTIC_FORCE_INCREMENT ) );
}
protected LockingStrategy generateLocker(LockMode lockMode) {
return factory.getDialect().getLockingStrategy( this, lockMode );
}
private LockingStrategy getLocker(LockMode lockMode) {
return ( LockingStrategy ) lockers.get( lockMode );
}
public void lock(
Serializable id,
Object version,
Object object,
LockMode lockMode,
SessionImplementor session) throws HibernateException {
getLocker( lockMode ).lock( id, version, object, LockOptions.WAIT_FOREVER, session );
}
public void lock(
Serializable id,
Object version,
Object object,
LockOptions lockOptions,
SessionImplementor session) throws HibernateException {
getLocker( lockOptions.getLockMode() ).lock( id, version, object, lockOptions.getTimeOut(), session );
}
public String getRootTableName() {
return getSubclassTableName( 0 );
}
public String getRootTableAlias(String drivingAlias) {
return drivingAlias;
}
public String[] getRootTableIdentifierColumnNames() {
return getRootTableKeyColumnNames();
}
public String[] toColumns(String alias, String propertyName) throws QueryException {
return propertyMapping.toColumns( alias, propertyName );
}
public String[] toColumns(String propertyName) throws QueryException {
return propertyMapping.getColumnNames( propertyName );
}
public Type toType(String propertyName) throws QueryException {
return propertyMapping.toType( propertyName );
}
public String[] getPropertyColumnNames(String propertyName) {
return propertyMapping.getColumnNames( propertyName );
}
/**
* Warning:
* When there are duplicated property names in the subclasses
* of the class, this method may return the wrong table
* number for the duplicated subclass property (note that
* SingleTableEntityPersister defines an overloaded form
* which takes the entity name.
*/
public int getSubclassPropertyTableNumber(String propertyPath) {
String rootPropertyName = StringHelper.root(propertyPath);
Type type = propertyMapping.toType(rootPropertyName);
if ( type.isAssociationType() ) {
AssociationType assocType = ( AssociationType ) type;
if ( assocType.useLHSPrimaryKey() ) {
// performance op to avoid the array search
return 0;
}
else if ( type.isCollectionType() ) {
// properly handle property-ref-based associations
rootPropertyName = assocType.getLHSPropertyName();
}
}
//Enable for HHH-440, which we don't like:
/*if ( type.isComponentType() && !propertyName.equals(rootPropertyName) ) {
String unrooted = StringHelper.unroot(propertyName);
int idx = ArrayHelper.indexOf( getSubclassColumnClosure(), unrooted );
if ( idx != -1 ) {
return getSubclassColumnTableNumberClosure()[idx];
}
}*/
int index = ArrayHelper.indexOf( getSubclassPropertyNameClosure(), rootPropertyName); //TODO: optimize this better!
return index==-1 ? 0 : getSubclassPropertyTableNumber(index);
}
public Declarer getSubclassPropertyDeclarer(String propertyPath) {
int tableIndex = getSubclassPropertyTableNumber( propertyPath );
if ( tableIndex == 0 ) {
return Declarer.CLASS;
}
else if ( isClassOrSuperclassTable( tableIndex ) ) {
return Declarer.SUPERCLASS;
}
else {
return Declarer.SUBCLASS;
}
}
private DiscriminatorMetadata discriminatorMetadata;
public DiscriminatorMetadata getTypeDiscriminatorMetadata() {
if ( discriminatorMetadata == null ) {
discriminatorMetadata = buildTypeDiscriminatorMetadata();
}
return discriminatorMetadata;
}
private DiscriminatorMetadata buildTypeDiscriminatorMetadata() {
return new DiscriminatorMetadata() {
public String getSqlFragment(String sqlQualificationAlias) {
return toColumns( sqlQualificationAlias, ENTITY_CLASS )[0];
}
public Type getResolutionType() {
return new DiscriminatorType( getDiscriminatorType(), AbstractEntityPersister.this );
}
};
}
public static String generateTableAlias(String rootAlias, int tableNumber) {
if ( tableNumber == 0 ) {
return rootAlias;
}
StringBuilder buf = new StringBuilder().append( rootAlias );
if ( !rootAlias.endsWith( "_" ) ) {
buf.append( '_' );
}
return buf.append( tableNumber ).append( '_' ).toString();
}
public String[] toColumns(String name, final int i) {
final String alias = generateTableAlias( name, getSubclassPropertyTableNumber( i ) );
String[] cols = getSubclassPropertyColumnNames( i );
String[] templates = getSubclassPropertyFormulaTemplateClosure()[i];
String[] result = new String[cols.length];
for ( int j = 0; j < cols.length; j++ ) {
if ( cols[j] == null ) {
result[j] = StringHelper.replace( templates[j], Template.TEMPLATE, alias );
}
else {
result[j] = StringHelper.qualify( alias, cols[j] );
}
}
return result;
}
private int getSubclassPropertyIndex(String propertyName) {
return ArrayHelper.indexOf(subclassPropertyNameClosure, propertyName);
}
protected String[] getPropertySubclassNames() {
return propertySubclassNames;
}
public String[] getPropertyColumnNames(int i) {
return propertyColumnNames[i];
}
public String[] getPropertyColumnWriters(int i) {
return propertyColumnWriters[i];
}
protected int getPropertyColumnSpan(int i) {
return propertyColumnSpans[i];
}
protected boolean hasFormulaProperties() {
return hasFormulaProperties;
}
public FetchMode getFetchMode(int i) {
return subclassPropertyFetchModeClosure[i];
}
public CascadeStyle getCascadeStyle(int i) {
return subclassPropertyCascadeStyleClosure[i];
}
public Type getSubclassPropertyType(int i) {
return subclassPropertyTypeClosure[i];
}
public String getSubclassPropertyName(int i) {
return subclassPropertyNameClosure[i];
}
public int countSubclassProperties() {
return subclassPropertyTypeClosure.length;
}
public String[] getSubclassPropertyColumnNames(int i) {
return subclassPropertyColumnNameClosure[i];
}
public boolean isDefinedOnSubclass(int i) {
return propertyDefinedOnSubclass[i];
}
@Override
public String[][] getSubclassPropertyFormulaTemplateClosure() {
return subclassPropertyFormulaTemplateClosure;
}
protected Type[] getSubclassPropertyTypeClosure() {
return subclassPropertyTypeClosure;
}
protected String[][] getSubclassPropertyColumnNameClosure() {
return subclassPropertyColumnNameClosure;
}
public String[][] getSubclassPropertyColumnReaderClosure() {
return subclassPropertyColumnReaderClosure;
}
public String[][] getSubclassPropertyColumnReaderTemplateClosure() {
return subclassPropertyColumnReaderTemplateClosure;
}
protected String[] getSubclassPropertyNameClosure() {
return subclassPropertyNameClosure;
}
@Override
public int[] resolveAttributeIndexes(Set properties) {
Iterator iter = properties.iterator();
int[] fields = new int[properties.size()];
int counter = 0;
while(iter.hasNext()) {
Integer index = entityMetamodel.getPropertyIndexOrNull( iter.next() );
if ( index != null )
fields[counter++] = index;
}
return fields;
}
protected String[] getSubclassPropertySubclassNameClosure() {
return subclassPropertySubclassNameClosure;
}
protected String[] getSubclassColumnClosure() {
return subclassColumnClosure;
}
protected String[] getSubclassColumnAliasClosure() {
return subclassColumnAliasClosure;
}
public String[] getSubclassColumnReaderTemplateClosure() {
return subclassColumnReaderTemplateClosure;
}
protected String[] getSubclassFormulaClosure() {
return subclassFormulaClosure;
}
protected String[] getSubclassFormulaTemplateClosure() {
return subclassFormulaTemplateClosure;
}
protected String[] getSubclassFormulaAliasClosure() {
return subclassFormulaAliasClosure;
}
public String[] getSubclassPropertyColumnAliases(String propertyName, String suffix) {
String[] rawAliases = ( String[] ) subclassPropertyAliases.get( propertyName );
if ( rawAliases == null ) {
return null;
}
String[] result = new String[rawAliases.length];
for ( int i = 0; i < rawAliases.length; i++ ) {
result[i] = new Alias( suffix ).toUnquotedAliasString( rawAliases[i] );
}
return result;
}
public String[] getSubclassPropertyColumnNames(String propertyName) {
//TODO: should we allow suffixes on these ?
return ( String[] ) subclassPropertyColumnNames.get( propertyName );
}
//This is really ugly, but necessary:
/**
* Must be called by subclasses, at the end of their constructors
*/
protected void initSubclassPropertyAliasesMap(PersistentClass model) throws MappingException {
// ALIASES
internalInitSubclassPropertyAliasesMap( null, model.getSubclassPropertyClosureIterator() );
// aliases for identifier ( alias.id ); skip if the entity defines a non-id property named 'id'
if ( ! entityMetamodel.hasNonIdentifierPropertyNamedId() ) {
subclassPropertyAliases.put( ENTITY_ID, getIdentifierAliases() );
subclassPropertyColumnNames.put( ENTITY_ID, getIdentifierColumnNames() );
}
// aliases named identifier ( alias.idname )
if ( hasIdentifierProperty() ) {
subclassPropertyAliases.put( getIdentifierPropertyName(), getIdentifierAliases() );
subclassPropertyColumnNames.put( getIdentifierPropertyName(), getIdentifierColumnNames() );
}
// aliases for composite-id's
if ( getIdentifierType().isComponentType() ) {
// Fetch embedded identifiers propertynames from the "virtual" identifier component
CompositeType componentId = ( CompositeType ) getIdentifierType();
String[] idPropertyNames = componentId.getPropertyNames();
String[] idAliases = getIdentifierAliases();
String[] idColumnNames = getIdentifierColumnNames();
for ( int i = 0; i < idPropertyNames.length; i++ ) {
if ( entityMetamodel.hasNonIdentifierPropertyNamedId() ) {
subclassPropertyAliases.put(
ENTITY_ID + "." + idPropertyNames[i],
new String[] { idAliases[i] }
);
subclassPropertyColumnNames.put(
ENTITY_ID + "." + getIdentifierPropertyName() + "." + idPropertyNames[i],
new String[] { idColumnNames[i] }
);
}
// if (hasIdentifierProperty() && !ENTITY_ID.equals( getIdentifierPropertyName() ) ) {
if ( hasIdentifierProperty() ) {
subclassPropertyAliases.put(
getIdentifierPropertyName() + "." + idPropertyNames[i],
new String[] { idAliases[i] }
);
subclassPropertyColumnNames.put(
getIdentifierPropertyName() + "." + idPropertyNames[i],
new String[] { idColumnNames[i] }
);
}
else {
// embedded composite ids ( alias.idname1, alias.idname2 )
subclassPropertyAliases.put( idPropertyNames[i], new String[] { idAliases[i] } );
subclassPropertyColumnNames.put( idPropertyNames[i], new String[] { idColumnNames[i] } );
}
}
}
if ( entityMetamodel.isPolymorphic() ) {
subclassPropertyAliases.put( ENTITY_CLASS, new String[] { getDiscriminatorAlias() } );
subclassPropertyColumnNames.put( ENTITY_CLASS, new String[] { getDiscriminatorColumnName() } );
}
}
/**
* Must be called by subclasses, at the end of their constructors
*/
protected void initSubclassPropertyAliasesMap(EntityBinding model) throws MappingException {
// ALIASES
// TODO: Fix when subclasses are working (HHH-6337)
//internalInitSubclassPropertyAliasesMap( null, model.getSubclassPropertyClosureIterator() );
// aliases for identifier ( alias.id ); skip if the entity defines a non-id property named 'id'
if ( ! entityMetamodel.hasNonIdentifierPropertyNamedId() ) {
subclassPropertyAliases.put( ENTITY_ID, getIdentifierAliases() );
subclassPropertyColumnNames.put( ENTITY_ID, getIdentifierColumnNames() );
}
// aliases named identifier ( alias.idname )
if ( hasIdentifierProperty() ) {
subclassPropertyAliases.put( getIdentifierPropertyName(), getIdentifierAliases() );
subclassPropertyColumnNames.put( getIdentifierPropertyName(), getIdentifierColumnNames() );
}
// aliases for composite-id's
if ( getIdentifierType().isComponentType() ) {
// Fetch embedded identifiers propertynames from the "virtual" identifier component
CompositeType componentId = ( CompositeType ) getIdentifierType();
String[] idPropertyNames = componentId.getPropertyNames();
String[] idAliases = getIdentifierAliases();
String[] idColumnNames = getIdentifierColumnNames();
for ( int i = 0; i < idPropertyNames.length; i++ ) {
if ( entityMetamodel.hasNonIdentifierPropertyNamedId() ) {
subclassPropertyAliases.put(
ENTITY_ID + "." + idPropertyNames[i],
new String[] { idAliases[i] }
);
subclassPropertyColumnNames.put(
ENTITY_ID + "." + getIdentifierPropertyName() + "." + idPropertyNames[i],
new String[] { idColumnNames[i] }
);
}
// if (hasIdentifierProperty() && !ENTITY_ID.equals( getIdentifierPropertyName() ) ) {
if ( hasIdentifierProperty() ) {
subclassPropertyAliases.put(
getIdentifierPropertyName() + "." + idPropertyNames[i],
new String[] { idAliases[i] }
);
subclassPropertyColumnNames.put(
getIdentifierPropertyName() + "." + idPropertyNames[i],
new String[] { idColumnNames[i] }
);
}
else {
// embedded composite ids ( alias.idname1, alias.idname2 )
subclassPropertyAliases.put( idPropertyNames[i], new String[] { idAliases[i] } );
subclassPropertyColumnNames.put( idPropertyNames[i], new String[] { idColumnNames[i] } );
}
}
}
if ( entityMetamodel.isPolymorphic() ) {
subclassPropertyAliases.put( ENTITY_CLASS, new String[] { getDiscriminatorAlias() } );
subclassPropertyColumnNames.put( ENTITY_CLASS, new String[] { getDiscriminatorColumnName() } );
}
}
private void internalInitSubclassPropertyAliasesMap(String path, Iterator propertyIterator) {
while ( propertyIterator.hasNext() ) {
Property prop = ( Property ) propertyIterator.next();
String propname = path == null ? prop.getName() : path + "." + prop.getName();
if ( prop.isComposite() ) {
Component component = ( Component ) prop.getValue();
Iterator compProps = component.getPropertyIterator();
internalInitSubclassPropertyAliasesMap( propname, compProps );
}
else {
String[] aliases = new String[prop.getColumnSpan()];
String[] cols = new String[prop.getColumnSpan()];
Iterator colIter = prop.getColumnIterator();
int l = 0;
while ( colIter.hasNext() ) {
Selectable thing = ( Selectable ) colIter.next();
aliases[l] = thing.getAlias( getFactory().getDialect(), prop.getValue().getTable() );
cols[l] = thing.getText( getFactory().getDialect() ); // TODO: skip formulas?
l++;
}
subclassPropertyAliases.put( propname, aliases );
subclassPropertyColumnNames.put( propname, cols );
}
}
}
public Object loadByUniqueKey(
String propertyName,
Object uniqueKey,
SessionImplementor session) throws HibernateException {
return getAppropriateUniqueKeyLoader( propertyName, session ).loadByUniqueKey( session, uniqueKey );
}
private EntityLoader getAppropriateUniqueKeyLoader(String propertyName, SessionImplementor session) {
final boolean useStaticLoader = !session.getLoadQueryInfluencers().hasEnabledFilters()
&& !session.getLoadQueryInfluencers().hasEnabledFetchProfiles()
&& propertyName.indexOf('.')<0; //ugly little workaround for fact that createUniqueKeyLoaders() does not handle component properties
if ( useStaticLoader ) {
return ( EntityLoader ) uniqueKeyLoaders.get( propertyName );
}
else {
return createUniqueKeyLoader(
propertyMapping.toType( propertyName ),
propertyMapping.toColumns( propertyName ),
session.getLoadQueryInfluencers()
);
}
}
public int getPropertyIndex(String propertyName) {
return entityMetamodel.getPropertyIndex(propertyName);
}
protected void createUniqueKeyLoaders() throws MappingException {
Type[] propertyTypes = getPropertyTypes();
String[] propertyNames = getPropertyNames();
for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
if ( propertyUniqueness[i] ) {
//don't need filters for the static loaders
uniqueKeyLoaders.put(
propertyNames[i],
createUniqueKeyLoader(
propertyTypes[i],
getPropertyColumnNames( i ),
LoadQueryInfluencers.NONE
)
);
//TODO: create uk loaders for component properties
}
}
}
private EntityLoader createUniqueKeyLoader(
Type uniqueKeyType,
String[] columns,
LoadQueryInfluencers loadQueryInfluencers) {
if ( uniqueKeyType.isEntityType() ) {
String className = ( ( EntityType ) uniqueKeyType ).getAssociatedEntityName();
uniqueKeyType = getFactory().getEntityPersister( className ).getIdentifierType();
}
return new EntityLoader(
this,
columns,
uniqueKeyType,
1,
LockMode.NONE,
getFactory(),
loadQueryInfluencers
);
}
protected String getSQLWhereString(String alias) {
return StringHelper.replace( sqlWhereStringTemplate, Template.TEMPLATE, alias );
}
protected boolean hasWhere() {
return sqlWhereString != null;
}
private void initOrdinaryPropertyPaths(Mapping mapping) throws MappingException {
for ( int i = 0; i < getSubclassPropertyNameClosure().length; i++ ) {
propertyMapping.initPropertyPaths( getSubclassPropertyNameClosure()[i],
getSubclassPropertyTypeClosure()[i],
getSubclassPropertyColumnNameClosure()[i],
getSubclassPropertyColumnReaderClosure()[i],
getSubclassPropertyColumnReaderTemplateClosure()[i],
getSubclassPropertyFormulaTemplateClosure()[i],
mapping );
}
}
private void initIdentifierPropertyPaths(Mapping mapping) throws MappingException {
String idProp = getIdentifierPropertyName();
if ( idProp != null ) {
propertyMapping.initPropertyPaths( idProp, getIdentifierType(), getIdentifierColumnNames(),
getIdentifierColumnReaders(), getIdentifierColumnReaderTemplates(), null, mapping );
}
if ( entityMetamodel.getIdentifierProperty().isEmbedded() ) {
propertyMapping.initPropertyPaths( null, getIdentifierType(), getIdentifierColumnNames(),
getIdentifierColumnReaders(), getIdentifierColumnReaderTemplates(), null, mapping );
}
if ( ! entityMetamodel.hasNonIdentifierPropertyNamedId() ) {
propertyMapping.initPropertyPaths( ENTITY_ID, getIdentifierType(), getIdentifierColumnNames(),
getIdentifierColumnReaders(), getIdentifierColumnReaderTemplates(), null, mapping );
}
}
private void initDiscriminatorPropertyPath(Mapping mapping) throws MappingException {
propertyMapping.initPropertyPaths( ENTITY_CLASS,
getDiscriminatorType(),
new String[]{getDiscriminatorColumnName()},
new String[]{getDiscriminatorColumnReaders()},
new String[]{getDiscriminatorColumnReaderTemplate()},
new String[]{getDiscriminatorFormulaTemplate()},
getFactory() );
}
protected void initPropertyPaths(Mapping mapping) throws MappingException {
initOrdinaryPropertyPaths(mapping);
initOrdinaryPropertyPaths(mapping); //do two passes, for collection property-ref!
initIdentifierPropertyPaths(mapping);
if ( entityMetamodel.isPolymorphic() ) {
initDiscriminatorPropertyPath( mapping );
}
}
protected UniqueEntityLoader createEntityLoader(
LockMode lockMode,
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
//TODO: disable batch loading if lockMode > READ?
return BatchingEntityLoaderBuilder.getBuilder( getFactory() )
.buildLoader( this, batchSize, lockMode, getFactory(), loadQueryInfluencers );
}
protected UniqueEntityLoader createEntityLoader(
LockOptions lockOptions,
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
//TODO: disable batch loading if lockMode > READ?
return BatchingEntityLoaderBuilder.getBuilder( getFactory() )
.buildLoader( this, batchSize, lockOptions, getFactory(), loadQueryInfluencers );
}
/**
* Used internally to create static loaders. These are the default set of loaders used to handle get()/load()
* processing. lock() handling is done by the LockingStrategy instances (see {@link #getLocker})
*
* @param lockMode The lock mode to apply to the thing being loaded.
* @return
*
* @throws MappingException
*/
protected UniqueEntityLoader createEntityLoader(LockMode lockMode) throws MappingException {
return createEntityLoader( lockMode, LoadQueryInfluencers.NONE );
}
protected boolean check(int rows, Serializable id, int tableNumber, Expectation expectation, PreparedStatement statement) throws HibernateException {
try {
expectation.verifyOutcome( rows, statement, -1 );
}
catch( StaleStateException e ) {
if ( !isNullableTable( tableNumber ) ) {
if ( getFactory().getStatistics().isStatisticsEnabled() ) {
getFactory().getStatisticsImplementor()
.optimisticFailure( getEntityName() );
}
throw new StaleObjectStateException( getEntityName(), id );
}
return false;
}
catch( TooManyRowsAffectedException e ) {
throw new HibernateException(
"Duplicate identifier in table for: " +
MessageHelper.infoString( this, id, getFactory() )
);
}
catch ( Throwable t ) {
return false;
}
return true;
}
protected String generateUpdateString(boolean[] includeProperty, int j, boolean useRowId) {
return generateUpdateString( includeProperty, j, null, useRowId );
}
/**
* Generate the SQL that updates a row by id (and version)
*/
protected String generateUpdateString(final boolean[] includeProperty,
final int j,
final Object[] oldFields,
final boolean useRowId) {
Update update = new Update( getFactory().getDialect() ).setTableName( getTableName( j ) );
// select the correct row by either pk or rowid
if ( useRowId ) {
update.addPrimaryKeyColumns( new String[]{rowIdName} ); //TODO: eventually, rowIdName[j]
}
else {
update.addPrimaryKeyColumns( getKeyColumns( j ) );
}
boolean hasColumns = false;
for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
if ( includeProperty[i] && isPropertyOfTable( i, j )
&& !lobProperties.contains( i ) ) {
// this is a property of the table, which we are updating
update.addColumns( getPropertyColumnNames(i),
propertyColumnUpdateable[i], propertyColumnWriters[i] );
hasColumns = hasColumns || getPropertyColumnSpan( i ) > 0;
}
}
// HHH-4635
// Oracle expects all Lob properties to be last in inserts
// and updates. Insert them at the end.
for ( int i : lobProperties ) {
if ( includeProperty[i] && isPropertyOfTable( i, j ) ) {
// this property belongs on the table and is to be inserted
update.addColumns( getPropertyColumnNames(i),
propertyColumnUpdateable[i], propertyColumnWriters[i] );
hasColumns = true;
}
}
if ( j == 0 && isVersioned() && entityMetamodel.getOptimisticLockStyle() == OptimisticLockStyle.VERSION ) {
// this is the root (versioned) table, and we are using version-based
// optimistic locking; if we are not updating the version, also don't
// check it (unless this is a "generated" version column)!
if ( checkVersion( includeProperty ) ) {
update.setVersionColumnName( getVersionColumnName() );
hasColumns = true;
}
}
else if ( isAllOrDirtyOptLocking() && oldFields != null ) {
// we are using "all" or "dirty" property-based optimistic locking
boolean[] includeInWhere = entityMetamodel.getOptimisticLockStyle() == OptimisticLockStyle.ALL
? getPropertyUpdateability() //optimistic-lock="all", include all updatable properties
: includeProperty; //optimistic-lock="dirty", include all properties we are updating this time
boolean[] versionability = getPropertyVersionability();
Type[] types = getPropertyTypes();
for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
boolean include = includeInWhere[i] &&
isPropertyOfTable( i, j ) &&
versionability[i];
if ( include ) {
// this property belongs to the table, and it is not specifically
// excluded from optimistic locking by optimistic-lock="false"
String[] propertyColumnNames = getPropertyColumnNames( i );
String[] propertyColumnWriters = getPropertyColumnWriters( i );
boolean[] propertyNullness = types[i].toColumnNullness( oldFields[i], getFactory() );
for ( int k=0; k mapping;
// this code assumes that optional defaults to "true" because it
// doesn't actually seem to work in the fetch="join" code
//
// Note that actual proper handling of optional-ality here is actually
// more involved than this patch assumes. Remember that we might have
// multiple mappings associated with a single entity. Really
// a couple of things need to happen to properly handle optional here:
// 1) First and foremost, when handling multiple s, we really
// should be using the entity root table as the driving table;
// another option here would be to choose some non-optional joined
// table to use as the driving table. In all likelihood, just using
// the root table is much simplier
// 2) Need to add the FK columns corresponding to each joined table
// to the generated select list; these would then be used when
// iterating the result set to determine whether all non-optional
// data is present
// My initial thoughts on the best way to deal with this would be
// to introduce a new SequentialSelect abstraction that actually gets
// generated in the persisters (ok, SingleTable...) and utilized here.
// It would encapsulated all this required optional-ality checking...
sequentialSelectEmpty = true;
}
}
}
final String[] propNames = getPropertyNames();
final Type[] types = getPropertyTypes();
final Object[] values = new Object[types.length];
final boolean[] laziness = getPropertyLaziness();
final String[] propSubclassNames = getSubclassPropertySubclassNameClosure();
for ( int i = 0; i < types.length; i++ ) {
if ( !propertySelectable[i] ) {
values[i] = BackrefPropertyAccessor.UNKNOWN;
}
else if ( allProperties || !laziness[i] ) {
//decide which ResultSet to get the property value from:
final boolean propertyIsDeferred = hasDeferred &&
rootPersister.isSubclassPropertyDeferred( propNames[i], propSubclassNames[i] );
if ( propertyIsDeferred && sequentialSelectEmpty ) {
values[i] = null;
}
else {
final ResultSet propertyResultSet = propertyIsDeferred ? sequentialResultSet : rs;
final String[] cols = propertyIsDeferred ? propertyColumnAliases[i] : suffixedPropertyColumns[i];
values[i] = types[i].hydrate( propertyResultSet, cols, session, object );
}
}
else {
values[i] = LazyPropertyInitializer.UNFETCHED_PROPERTY;
}
}
if ( sequentialResultSet != null ) {
session.getTransactionCoordinator().getJdbcCoordinator().release( sequentialResultSet, sequentialSelect );
}
return values;
}
finally {
if ( sequentialSelect != null ) {
session.getTransactionCoordinator().getJdbcCoordinator().release( sequentialSelect );
}
}
}
protected boolean useInsertSelectIdentity() {
return !useGetGeneratedKeys() && getFactory().getDialect().supportsInsertSelectIdentity();
}
protected boolean useGetGeneratedKeys() {
return getFactory().getSettings().isGetGeneratedKeysEnabled();
}
protected String getSequentialSelect(String entityName) {
throw new UnsupportedOperationException("no sequential selects");
}
/**
* Perform an SQL INSERT, and then retrieve a generated identifier.
*
* This form is used for PostInsertIdentifierGenerator-style ids (IDENTITY,
* select, etc).
*/
protected Serializable insert(
final Object[] fields,
final boolean[] notNull,
String sql,
final Object object,
final SessionImplementor session) throws HibernateException {
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Inserting entity: {0} (native id)", getEntityName() );
if ( isVersioned() ) {
LOG.tracev( "Version: {0}", Versioning.getVersion( fields, this ) );
}
}
Binder binder = new Binder() {
public void bindValues(PreparedStatement ps) throws SQLException {
dehydrate( null, fields, notNull, propertyColumnInsertable, 0, ps, session, false );
}
public Object getEntity() {
return object;
}
};
return identityDelegate.performInsert( sql, session, binder );
}
public String getIdentitySelectString() {
//TODO: cache this in an instvar
return getFactory().getDialect().getIdentitySelectString(
getTableName(0),
getKeyColumns(0)[0],
getIdentifierType().sqlTypes( getFactory() )[0]
);
}
public String getSelectByUniqueKeyString(String propertyName) {
return new SimpleSelect( getFactory().getDialect() )
.setTableName( getTableName(0) )
.addColumns( getKeyColumns(0) )
.addCondition( getPropertyColumnNames(propertyName), "=?" )
.toStatementString();
}
private BasicBatchKey inserBatchKey;
/**
* Perform an SQL INSERT.
*
* This for is used for all non-root tables as well as the root table
* in cases where the identifier value is known before the insert occurs.
*/
protected void insert(
final Serializable id,
final Object[] fields,
final boolean[] notNull,
final int j,
final String sql,
final Object object,
final SessionImplementor session) throws HibernateException {
if ( isInverseTable( j ) ) {
return;
}
//note: it is conceptually possible that a UserType could map null to
// a non-null value, so the following is arguable:
if ( isNullableTable( j ) && isAllNull( fields, j ) ) {
return;
}
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Inserting entity: {0}", MessageHelper.infoString( this, id, getFactory() ) );
if ( j == 0 && isVersioned() )
LOG.tracev( "Version: {0}", Versioning.getVersion( fields, this ) );
}
// TODO : shouldn't inserts be Expectations.NONE?
final Expectation expectation = Expectations.appropriateExpectation( insertResultCheckStyles[j] );
// we can't batch joined inserts, *especially* not if it is an identity insert;
// nor can we batch statements where the expectation is based on an output param
final boolean useBatch = j == 0 && expectation.canBeBatched();
if ( useBatch && inserBatchKey == null ) {
inserBatchKey = new BasicBatchKey(
getEntityName() + "#INSERT",
expectation
);
}
final boolean callable = isInsertCallable( j );
try {
// Render the SQL query
final PreparedStatement insert;
if ( useBatch ) {
insert = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getBatch( inserBatchKey )
.getBatchStatement( sql, callable );
}
else {
insert = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql, callable );
}
try {
int index = 1;
index += expectation.prepare( insert );
// Write the values of fields onto the prepared statement - we MUST use the state at the time the
// insert was issued (cos of foreign key constraints). Not necessarily the object's current state
dehydrate( id, fields, null, notNull, propertyColumnInsertable, j, insert, session, index, false );
if ( useBatch ) {
session.getTransactionCoordinator().getJdbcCoordinator().getBatch( inserBatchKey ).addToBatch();
}
else {
expectation.verifyOutcome( session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().executeUpdate( insert ), insert, -1 );
}
}
catch ( SQLException e ) {
if ( useBatch ) {
session.getTransactionCoordinator().getJdbcCoordinator().abortBatch();
}
throw e;
}
finally {
if ( !useBatch ) {
session.getTransactionCoordinator().getJdbcCoordinator().release( insert );
}
}
}
catch ( SQLException e ) {
throw getFactory().getSQLExceptionHelper().convert(
e,
"could not insert: " + MessageHelper.infoString( this ),
sql
);
}
}
/**
* Perform an SQL UPDATE or SQL INSERT
*/
protected void updateOrInsert(
final Serializable id,
final Object[] fields,
final Object[] oldFields,
final Object rowId,
final boolean[] includeProperty,
final int j,
final Object oldVersion,
final Object object,
final String sql,
final SessionImplementor session) throws HibernateException {
if ( !isInverseTable( j ) ) {
final boolean isRowToUpdate;
if ( isNullableTable( j ) && oldFields != null && isAllNull( oldFields, j ) ) {
//don't bother trying to update, we know there is no row there yet
isRowToUpdate = false;
}
else if ( isNullableTable( j ) && isAllNull( fields, j ) ) {
//if all fields are null, we might need to delete existing row
isRowToUpdate = true;
delete( id, oldVersion, j, object, getSQLDeleteStrings()[j], session, null );
}
else {
//there is probably a row there, so try to update
//if no rows were updated, we will find out
isRowToUpdate = update( id, fields, oldFields, rowId, includeProperty, j, oldVersion, object, sql, session );
}
if ( !isRowToUpdate && !isAllNull( fields, j ) ) {
// assume that the row was not there since it previously had only null
// values, so do an INSERT instead
//TODO: does not respect dynamic-insert
insert( id, fields, getPropertyInsertability(), j, getSQLInsertStrings()[j], object, session );
}
}
}
private BasicBatchKey updateBatchKey;
protected boolean update(
final Serializable id,
final Object[] fields,
final Object[] oldFields,
final Object rowId,
final boolean[] includeProperty,
final int j,
final Object oldVersion,
final Object object,
final String sql,
final SessionImplementor session) throws HibernateException {
final Expectation expectation = Expectations.appropriateExpectation( updateResultCheckStyles[j] );
final boolean useBatch = j == 0 && expectation.canBeBatched() && isBatchable(); //note: updates to joined tables can't be batched...
if ( useBatch && updateBatchKey == null ) {
updateBatchKey = new BasicBatchKey(
getEntityName() + "#UPDATE",
expectation
);
}
final boolean callable = isUpdateCallable( j );
final boolean useVersion = j == 0 && isVersioned();
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Updating entity: {0}", MessageHelper.infoString( this, id, getFactory() ) );
if ( useVersion )
LOG.tracev( "Existing version: {0} -> New version:{1}", oldVersion, fields[getVersionProperty()] );
}
try {
int index = 1; // starting index
final PreparedStatement update;
if ( useBatch ) {
update = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getBatch( updateBatchKey )
.getBatchStatement( sql, callable );
}
else {
update = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql, callable );
}
try {
index+= expectation.prepare( update );
//Now write the values of fields onto the prepared statement
index = dehydrate( id, fields, rowId, includeProperty, propertyColumnUpdateable, j, update, session, index, true );
// Write any appropriate versioning conditional parameters
if ( useVersion && entityMetamodel.getOptimisticLockStyle() == OptimisticLockStyle.VERSION ) {
if ( checkVersion( includeProperty ) ) {
getVersionType().nullSafeSet( update, oldVersion, index, session );
}
}
else if ( isAllOrDirtyOptLocking() && oldFields != null ) {
boolean[] versionability = getPropertyVersionability(); //TODO: is this really necessary????
boolean[] includeOldField = entityMetamodel.getOptimisticLockStyle() == OptimisticLockStyle.ALL
? getPropertyUpdateability()
: includeProperty;
Type[] types = getPropertyTypes();
for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
boolean include = includeOldField[i] &&
isPropertyOfTable( i, j ) &&
versionability[i]; //TODO: is this really necessary????
if ( include ) {
boolean[] settable = types[i].toColumnNullness( oldFields[i], getFactory() );
types[i].nullSafeSet(
update,
oldFields[i],
index,
settable,
session
);
index += ArrayHelper.countTrue(settable);
}
}
}
if ( useBatch ) {
session.getTransactionCoordinator().getJdbcCoordinator().getBatch( updateBatchKey ).addToBatch();
return true;
}
else {
return check( session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().executeUpdate( update ), id, j, expectation, update );
}
}
catch ( SQLException e ) {
if ( useBatch ) {
session.getTransactionCoordinator().getJdbcCoordinator().abortBatch();
}
throw e;
}
finally {
if ( !useBatch ) {
session.getTransactionCoordinator().getJdbcCoordinator().release( update );
}
}
}
catch ( SQLException e ) {
throw getFactory().getSQLExceptionHelper().convert(
e,
"could not update: " + MessageHelper.infoString( this, id, getFactory() ),
sql
);
}
}
private BasicBatchKey deleteBatchKey;
/**
* Perform an SQL DELETE
*/
protected void delete(
final Serializable id,
final Object version,
final int j,
final Object object,
final String sql,
final SessionImplementor session,
final Object[] loadedState) throws HibernateException {
if ( isInverseTable( j ) ) {
return;
}
final boolean useVersion = j == 0 && isVersioned();
final boolean callable = isDeleteCallable( j );
final Expectation expectation = Expectations.appropriateExpectation( deleteResultCheckStyles[j] );
final boolean useBatch = j == 0 && isBatchable() && expectation.canBeBatched();
if ( useBatch && deleteBatchKey == null ) {
deleteBatchKey = new BasicBatchKey(
getEntityName() + "#DELETE",
expectation
);
}
final boolean traceEnabled = LOG.isTraceEnabled();
if ( traceEnabled ) {
LOG.tracev( "Deleting entity: {0}", MessageHelper.infoString( this, id, getFactory() ) );
if ( useVersion )
LOG.tracev( "Version: {0}", version );
}
if ( isTableCascadeDeleteEnabled( j ) ) {
if ( traceEnabled ) {
LOG.tracev( "Delete handled by foreign key constraint: {0}", getTableName( j ) );
}
return; //EARLY EXIT!
}
try {
//Render the SQL query
PreparedStatement delete;
int index = 1;
if ( useBatch ) {
delete = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getBatch( deleteBatchKey )
.getBatchStatement( sql, callable );
}
else {
delete = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql, callable );
}
try {
index += expectation.prepare( delete );
// Do the key. The key is immutable so we can use the _current_ object state - not necessarily
// the state at the time the delete was issued
getIdentifierType().nullSafeSet( delete, id, index, session );
index += getIdentifierColumnSpan();
// We should use the _current_ object state (ie. after any updates that occurred during flush)
if ( useVersion ) {
getVersionType().nullSafeSet( delete, version, index, session );
}
else if ( isAllOrDirtyOptLocking() && loadedState != null ) {
boolean[] versionability = getPropertyVersionability();
Type[] types = getPropertyTypes();
for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
if ( isPropertyOfTable( i, j ) && versionability[i] ) {
// this property belongs to the table and it is not specifically
// excluded from optimistic locking by optimistic-lock="false"
boolean[] settable = types[i].toColumnNullness( loadedState[i], getFactory() );
types[i].nullSafeSet( delete, loadedState[i], index, settable, session );
index += ArrayHelper.countTrue( settable );
}
}
}
if ( useBatch ) {
session.getTransactionCoordinator().getJdbcCoordinator().getBatch( deleteBatchKey ).addToBatch();
}
else {
check( session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().executeUpdate( delete ), id, j, expectation, delete );
}
}
catch ( SQLException sqle ) {
if ( useBatch ) {
session.getTransactionCoordinator().getJdbcCoordinator().abortBatch();
}
throw sqle;
}
finally {
if ( !useBatch ) {
session.getTransactionCoordinator().getJdbcCoordinator().release( delete );
}
}
}
catch ( SQLException sqle ) {
throw getFactory().getSQLExceptionHelper().convert(
sqle,
"could not delete: " +
MessageHelper.infoString( this, id, getFactory() ),
sql
);
}
}
private String[] getUpdateStrings(boolean byRowId, boolean lazy) {
if ( byRowId ) {
return lazy ? getSQLLazyUpdateByRowIdStrings() : getSQLUpdateByRowIdStrings();
}
else {
return lazy ? getSQLLazyUpdateStrings() : getSQLUpdateStrings();
}
}
/**
* Update an object
*/
public void update(
final Serializable id,
final Object[] fields,
final int[] dirtyFields,
final boolean hasDirtyCollection,
final Object[] oldFields,
final Object oldVersion,
final Object object,
final Object rowId,
final SessionImplementor session) throws HibernateException {
// apply any pre-update in-memory value generation
if ( getEntityMetamodel().hasPreUpdateGeneratedValues() ) {
final InMemoryValueGenerationStrategy[] strategies = getEntityMetamodel().getInMemoryValueGenerationStrategies();
for ( int i = 0; i < strategies.length; i++ ) {
if ( strategies[i] != null && strategies[i].getGenerationTiming().includesUpdate() ) {
fields[i] = strategies[i].getValueGenerator().generateValue( (Session) session, object );
setPropertyValue( object, i, fields[i] );
// todo : probably best to add to dirtyFields if not-null
}
}
}
//note: dirtyFields==null means we had no snapshot, and we couldn't get one using select-before-update
// oldFields==null just means we had no snapshot to begin with (we might have used select-before-update to get the dirtyFields)
final boolean[] tableUpdateNeeded = getTableUpdateNeeded( dirtyFields, hasDirtyCollection );
final int span = getTableSpan();
final boolean[] propsToUpdate;
final String[] updateStrings;
EntityEntry entry = session.getPersistenceContext().getEntry( object );
// Ensure that an immutable or non-modifiable entity is not being updated unless it is
// in the process of being deleted.
if ( entry == null && ! isMutable() ) {
throw new IllegalStateException( "Updating immutable entity that is not in session yet!" );
}
if ( ( entityMetamodel.isDynamicUpdate() && dirtyFields != null ) ) {
// We need to generate the UPDATE SQL when dynamic-update="true"
propsToUpdate = getPropertiesToUpdate( dirtyFields, hasDirtyCollection );
// don't need to check laziness (dirty checking algorithm handles that)
updateStrings = new String[span];
for ( int j = 0; j < span; j++ ) {
updateStrings[j] = tableUpdateNeeded[j] ?
generateUpdateString( propsToUpdate, j, oldFields, j == 0 && rowId != null ) :
null;
}
}
else if ( ! isModifiableEntity( entry ) ) {
// We need to generate UPDATE SQL when a non-modifiable entity (e.g., read-only or immutable)
// needs:
// - to have references to transient entities set to null before being deleted
// - to have version incremented do to a "dirty" association
// If dirtyFields == null, then that means that there are no dirty properties to
// to be updated; an empty array for the dirty fields needs to be passed to
// getPropertiesToUpdate() instead of null.
propsToUpdate = getPropertiesToUpdate(
( dirtyFields == null ? ArrayHelper.EMPTY_INT_ARRAY : dirtyFields ),
hasDirtyCollection
);
// don't need to check laziness (dirty checking algorithm handles that)
updateStrings = new String[span];
for ( int j = 0; j < span; j++ ) {
updateStrings[j] = tableUpdateNeeded[j] ?
generateUpdateString( propsToUpdate, j, oldFields, j == 0 && rowId != null ) :
null;
}
}
else {
// For the case of dynamic-update="false", or no snapshot, we use the static SQL
updateStrings = getUpdateStrings(
rowId != null,
hasUninitializedLazyProperties( object )
);
propsToUpdate = getPropertyUpdateability( object );
}
for ( int j = 0; j < span; j++ ) {
// Now update only the tables with dirty properties (and the table with the version number)
if ( tableUpdateNeeded[j] ) {
updateOrInsert(
id,
fields,
oldFields,
j == 0 ? rowId : null,
propsToUpdate,
j,
oldVersion,
object,
updateStrings[j],
session
);
}
}
}
public Serializable insert(Object[] fields, Object object, SessionImplementor session)
throws HibernateException {
// apply any pre-insert in-memory value generation
preInsertInMemoryValueGeneration( fields, object, session );
final int span = getTableSpan();
final Serializable id;
if ( entityMetamodel.isDynamicInsert() ) {
// For the case of dynamic-insert="true", we need to generate the INSERT SQL
boolean[] notNull = getPropertiesToInsert( fields );
id = insert( fields, notNull, generateInsertString( true, notNull ), object, session );
for ( int j = 1; j < span; j++ ) {
insert( id, fields, notNull, j, generateInsertString( notNull, j ), object, session );
}
}
else {
// For the case of dynamic-insert="false", use the static SQL
id = insert( fields, getPropertyInsertability(), getSQLIdentityInsertString(), object, session );
for ( int j = 1; j < span; j++ ) {
insert( id, fields, getPropertyInsertability(), j, getSQLInsertStrings()[j], object, session );
}
}
return id;
}
public void insert(Serializable id, Object[] fields, Object object, SessionImplementor session) {
// apply any pre-insert in-memory value generation
preInsertInMemoryValueGeneration( fields, object, session );
final int span = getTableSpan();
if ( entityMetamodel.isDynamicInsert() ) {
// For the case of dynamic-insert="true", we need to generate the INSERT SQL
boolean[] notNull = getPropertiesToInsert( fields );
for ( int j = 0; j < span; j++ ) {
insert( id, fields, notNull, j, generateInsertString( notNull, j ), object, session );
}
}
else {
// For the case of dynamic-insert="false", use the static SQL
for ( int j = 0; j < span; j++ ) {
insert( id, fields, getPropertyInsertability(), j, getSQLInsertStrings()[j], object, session );
}
}
}
private void preInsertInMemoryValueGeneration(Object[] fields, Object object, SessionImplementor session) {
if ( getEntityMetamodel().hasPreInsertGeneratedValues() ) {
final InMemoryValueGenerationStrategy[] strategies = getEntityMetamodel().getInMemoryValueGenerationStrategies();
for ( int i = 0; i < strategies.length; i++ ) {
if ( strategies[i] != null && strategies[i].getGenerationTiming().includesInsert() ) {
fields[i] = strategies[i].getValueGenerator().generateValue( (Session) session, object );
setPropertyValue( object, i, fields[i] );
}
}
}
}
/**
* Delete an object
*/
public void delete(Serializable id, Object version, Object object, SessionImplementor session)
throws HibernateException {
final int span = getTableSpan();
boolean isImpliedOptimisticLocking = !entityMetamodel.isVersioned() && isAllOrDirtyOptLocking();
Object[] loadedState = null;
if ( isImpliedOptimisticLocking ) {
// need to treat this as if it where optimistic-lock="all" (dirty does *not* make sense);
// first we need to locate the "loaded" state
//
// Note, it potentially could be a proxy, so doAfterTransactionCompletion the location the safe way...
final EntityKey key = session.generateEntityKey( id, this );
Object entity = session.getPersistenceContext().getEntity( key );
if ( entity != null ) {
EntityEntry entry = session.getPersistenceContext().getEntry( entity );
loadedState = entry.getLoadedState();
}
}
final String[] deleteStrings;
if ( isImpliedOptimisticLocking && loadedState != null ) {
// we need to utilize dynamic delete statements
deleteStrings = generateSQLDeletStrings( loadedState );
}
else {
// otherwise, utilize the static delete statements
deleteStrings = getSQLDeleteStrings();
}
for ( int j = span - 1; j >= 0; j-- ) {
delete( id, version, j, object, deleteStrings[j], session, loadedState );
}
}
private boolean isAllOrDirtyOptLocking() {
return entityMetamodel.getOptimisticLockStyle() == OptimisticLockStyle.DIRTY
|| entityMetamodel.getOptimisticLockStyle() == OptimisticLockStyle.ALL;
}
private String[] generateSQLDeletStrings(Object[] loadedState) {
int span = getTableSpan();
String[] deleteStrings = new String[span];
for ( int j = span - 1; j >= 0; j-- ) {
Delete delete = new Delete()
.setTableName( getTableName( j ) )
.addPrimaryKeyColumns( getKeyColumns( j ) );
if ( getFactory().getSettings().isCommentsEnabled() ) {
delete.setComment( "delete " + getEntityName() + " [" + j + "]" );
}
boolean[] versionability = getPropertyVersionability();
Type[] types = getPropertyTypes();
for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
if ( isPropertyOfTable( i, j ) && versionability[i] ) {
// this property belongs to the table and it is not specifically
// excluded from optimistic locking by optimistic-lock="false"
String[] propertyColumnNames = getPropertyColumnNames( i );
boolean[] propertyNullness = types[i].toColumnNullness( loadedState[i], getFactory() );
for ( int k = 0; k < propertyNullness.length; k++ ) {
if ( propertyNullness[k] ) {
delete.addWhereFragment( propertyColumnNames[k] + " = ?" );
}
else {
delete.addWhereFragment( propertyColumnNames[k] + " is null" );
}
}
}
}
deleteStrings[j] = delete.toStatementString();
}
return deleteStrings;
}
protected void logStaticSQL() {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Static SQL for entity: %s", getEntityName() );
if ( sqlLazySelectString != null ) {
LOG.debugf( " Lazy select: %s", sqlLazySelectString );
}
if ( sqlVersionSelectString != null ) {
LOG.debugf( " Version select: %s", sqlVersionSelectString );
}
if ( sqlSnapshotSelectString != null ) {
LOG.debugf( " Snapshot select: %s", sqlSnapshotSelectString );
}
for ( int j = 0; j < getTableSpan(); j++ ) {
LOG.debugf( " Insert %s: %s", j, getSQLInsertStrings()[j] );
LOG.debugf( " Update %s: %s", j, getSQLUpdateStrings()[j] );
LOG.debugf( " Delete %s: %s", j, getSQLDeleteStrings()[j] );
}
if ( sqlIdentityInsertString != null ) {
LOG.debugf( " Identity insert: %s", sqlIdentityInsertString );
}
if ( sqlUpdateByRowIdString != null ) {
LOG.debugf( " Update by row id (all fields): %s", sqlUpdateByRowIdString );
}
if ( sqlLazyUpdateByRowIdString != null ) {
LOG.debugf( " Update by row id (non-lazy fields): %s", sqlLazyUpdateByRowIdString );
}
if ( sqlInsertGeneratedValuesSelectString != null ) {
LOG.debugf( " Insert-generated property select: %s", sqlInsertGeneratedValuesSelectString );
}
if ( sqlUpdateGeneratedValuesSelectString != null ) {
LOG.debugf( " Update-generated property select: %s", sqlUpdateGeneratedValuesSelectString );
}
}
}
@Override
public String filterFragment(String alias, Map enabledFilters) throws MappingException {
final 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 treatAsDeclarations) {
final StringBuilder sessionFilterFragment = new StringBuilder();
filterHelper.render( sessionFilterFragment, getFilterAliasGenerator(alias), enabledFilters );
return sessionFilterFragment.append( filterFragment( alias, treatAsDeclarations ) ).toString();
}
public String generateFilterConditionAlias(String rootAlias) {
return rootAlias;
}
public String oneToManyFilterFragment(String alias) throws MappingException {
return "";
}
@Override
public String oneToManyFilterFragment(String alias, Set treatAsDeclarations) {
return oneToManyFilterFragment( alias );
}
@Override
public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
// NOTE : Not calling createJoin here is just a performance optimization
return getSubclassTableSpan() == 1
? ""
: createJoin( alias, innerJoin, includeSubclasses, Collections.emptySet() ).toFromFragmentString();
}
@Override
public String fromJoinFragment(
String alias,
boolean innerJoin,
boolean includeSubclasses,
Set treatAsDeclarations) {
// NOTE : Not calling createJoin here is just a performance optimization
return getSubclassTableSpan() == 1
? ""
: createJoin( alias, innerJoin, includeSubclasses, treatAsDeclarations ).toFromFragmentString();
}
@Override
public String whereJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
// NOTE : Not calling createJoin here is just a performance optimization
return getSubclassTableSpan() == 1
? ""
: createJoin( alias, innerJoin, includeSubclasses, Collections.emptySet() ).toWhereFragmentString();
}
@Override
public String whereJoinFragment(
String alias,
boolean innerJoin,
boolean includeSubclasses,
Set treatAsDeclarations) {
// NOTE : Not calling createJoin here is just a performance optimization
return getSubclassTableSpan() == 1
? ""
: createJoin( alias, innerJoin, includeSubclasses, treatAsDeclarations ).toWhereFragmentString();
}
protected boolean isSubclassTableLazy(int j) {
return false;
}
protected JoinFragment createJoin(String name, boolean innerJoin, boolean includeSubclasses, Set treatAsDeclarations) {
// IMPL NOTE : all joins join to the pk of the driving table
final String[] idCols = StringHelper.qualify( name, getIdentifierColumnNames() );
final JoinFragment join = getFactory().getDialect().createOuterJoinFragment();
final int tableSpan = getSubclassTableSpan();
// IMPL NOTE : notice that we skip the first table; it is the driving table!
for ( int j = 1; j < tableSpan; j++ ) {
final JoinType joinType = determineSubclassTableJoinType(
j,
innerJoin,
includeSubclasses,
treatAsDeclarations
);
if ( joinType != null && joinType != JoinType.NONE ) {
join.addJoin(
getSubclassTableName( j ),
generateTableAlias( name, j ),
idCols,
getSubclassTableKeyColumns( j ),
joinType
);
}
}
return join;
}
protected JoinType determineSubclassTableJoinType(
int subclassTableNumber,
boolean canInnerJoin,
boolean includeSubclasses,
Set treatAsDeclarations) {
if ( isClassOrSuperclassTable( subclassTableNumber ) ) {
final boolean shouldInnerJoin = canInnerJoin
&& !isInverseTable( subclassTableNumber )
&& !isNullableTable( subclassTableNumber );
// the table is either this persister's driving table or (one of) its super class persister's driving
// tables which can be inner joined as long as the `shouldInnerJoin` condition resolves to true
return shouldInnerJoin ? JoinType.INNER_JOIN : JoinType.LEFT_OUTER_JOIN;
}
// otherwise we have a subclass table and need to look a little deeper...
// IMPL NOTE : By default includeSubclasses indicates that all subclasses should be joined and that each
// subclass ought to be joined by outer-join. However, TREAT-AS always requires that an inner-join be used
// so we give TREAT-AS higher precedence...
if ( isSubclassTableIndicatedByTreatAsDeclarations( subclassTableNumber, treatAsDeclarations ) ) {
return JoinType.INNER_JOIN;
}
if ( includeSubclasses
&& !isSubclassTableSequentialSelect( subclassTableNumber )
&& !isSubclassTableLazy( subclassTableNumber ) ) {
return JoinType.LEFT_OUTER_JOIN;
}
return JoinType.NONE;
}
protected boolean isSubclassTableIndicatedByTreatAsDeclarations(
int subclassTableNumber,
Set treatAsDeclarations) {
return false;
}
protected JoinFragment createJoin(int[] tableNumbers, String drivingAlias) {
final String[] keyCols = StringHelper.qualify( drivingAlias, getSubclassTableKeyColumns( tableNumbers[0] ) );
final JoinFragment jf = getFactory().getDialect().createOuterJoinFragment();
// IMPL NOTE : notice that we skip the first table; it is the driving table!
for ( int i = 1; i < tableNumbers.length; i++ ) {
final int j = tableNumbers[i];
jf.addJoin( getSubclassTableName( j ),
generateTableAlias( getRootAlias(), j ),
keyCols,
getSubclassTableKeyColumns( j ),
isInverseSubclassTable( j ) || isNullableSubclassTable( j )
? JoinType.LEFT_OUTER_JOIN
: JoinType.INNER_JOIN
);
}
return jf;
}
protected SelectFragment createSelect(final int[] subclassColumnNumbers,
final int[] subclassFormulaNumbers) {
SelectFragment selectFragment = new SelectFragment();
int[] columnTableNumbers = getSubclassColumnTableNumberClosure();
String[] columnAliases = getSubclassColumnAliasClosure();
String[] columnReaderTemplates = getSubclassColumnReaderTemplateClosure();
for ( int i = 0; i < subclassColumnNumbers.length; i++ ) {
int columnNumber = subclassColumnNumbers[i];
if ( subclassColumnSelectableClosure[columnNumber] ) {
final String subalias = generateTableAlias( getRootAlias(), columnTableNumbers[columnNumber] );
selectFragment.addColumnTemplate( subalias, columnReaderTemplates[columnNumber], columnAliases[columnNumber] );
}
}
int[] formulaTableNumbers = getSubclassFormulaTableNumberClosure();
String[] formulaTemplates = getSubclassFormulaTemplateClosure();
String[] formulaAliases = getSubclassFormulaAliasClosure();
for ( int i = 0; i < subclassFormulaNumbers.length; i++ ) {
int formulaNumber = subclassFormulaNumbers[i];
final String subalias = generateTableAlias( getRootAlias(), formulaTableNumbers[formulaNumber] );
selectFragment.addFormula( subalias, formulaTemplates[formulaNumber], formulaAliases[formulaNumber] );
}
return selectFragment;
}
protected String createFrom(int tableNumber, String alias) {
return getSubclassTableName( tableNumber ) + ' ' + alias;
}
protected String createWhereByKey(int tableNumber, String alias) {
//TODO: move to .sql package, and refactor with similar things!
return StringHelper.join( "=? and ",
StringHelper.qualify( alias, getSubclassTableKeyColumns( tableNumber ) ) ) + "=?";
}
protected String renderSelect(
final int[] tableNumbers,
final int[] columnNumbers,
final int[] formulaNumbers) {
Arrays.sort( tableNumbers ); //get 'em in the right order (not that it really matters)
//render the where and from parts
int drivingTable = tableNumbers[0];
final String drivingAlias = generateTableAlias( getRootAlias(), drivingTable ); //we *could* regerate this inside each called method!
final String where = createWhereByKey( drivingTable, drivingAlias );
final String from = createFrom( drivingTable, drivingAlias );
//now render the joins
JoinFragment jf = createJoin( tableNumbers, drivingAlias );
//now render the select clause
SelectFragment selectFragment = createSelect( columnNumbers, formulaNumbers );
//now tie it all together
Select select = new Select( getFactory().getDialect() );
select.setSelectClause( selectFragment.toFragmentString().substring( 2 ) );
select.setFromClause( from );
select.setWhereClause( where );
select.setOuterJoins( jf.toFromFragmentString(), jf.toWhereFragmentString() );
if ( getFactory().getSettings().isCommentsEnabled() ) {
select.setComment( "sequential select " + getEntityName() );
}
return select.toStatementString();
}
private String getRootAlias() {
return StringHelper.generateAlias( getEntityName() );
}
/**
* Post-construct is a callback for AbstractEntityPersister subclasses to call after they are all done with their
* constructor processing. It allows AbstractEntityPersister to extend its construction after all subclass-specific
* details have been handled.
*
* @param mapping The mapping
*
* @throws MappingException Indicates a problem accessing the Mapping
*/
protected void postConstruct(Mapping mapping) throws MappingException {
initPropertyPaths( mapping );
//doLateInit();
prepareEntityIdentifierDefinition();
}
private void doLateInit() {
//insert/update/delete SQL
final int joinSpan = getTableSpan();
sqlDeleteStrings = new String[joinSpan];
sqlInsertStrings = new String[joinSpan];
sqlUpdateStrings = new String[joinSpan];
sqlLazyUpdateStrings = new String[joinSpan];
sqlUpdateByRowIdString = rowIdName == null ?
null :
generateUpdateString( getPropertyUpdateability(), 0, true );
sqlLazyUpdateByRowIdString = rowIdName == null ?
null :
generateUpdateString( getNonLazyPropertyUpdateability(), 0, true );
for ( int j = 0; j < joinSpan; j++ ) {
sqlInsertStrings[j] = customSQLInsert[j] == null ?
generateInsertString( getPropertyInsertability(), j ) :
customSQLInsert[j];
sqlUpdateStrings[j] = customSQLUpdate[j] == null ?
generateUpdateString( getPropertyUpdateability(), j, false ) :
customSQLUpdate[j];
sqlLazyUpdateStrings[j] = customSQLUpdate[j] == null ?
generateUpdateString( getNonLazyPropertyUpdateability(), j, false ) :
customSQLUpdate[j];
sqlDeleteStrings[j] = customSQLDelete[j] == null ?
generateDeleteString( j ) :
customSQLDelete[j];
}
tableHasColumns = new boolean[joinSpan];
for ( int j = 0; j < joinSpan; j++ ) {
tableHasColumns[j] = sqlUpdateStrings[j] != null;
}
//select SQL
sqlSnapshotSelectString = generateSnapshotSelectString();
sqlLazySelectString = generateLazySelectString();
sqlVersionSelectString = generateSelectVersionString();
if ( hasInsertGeneratedProperties() ) {
sqlInsertGeneratedValuesSelectString = generateInsertGeneratedValuesSelectString();
}
if ( hasUpdateGeneratedProperties() ) {
sqlUpdateGeneratedValuesSelectString = generateUpdateGeneratedValuesSelectString();
}
if ( isIdentifierAssignedByInsert() ) {
identityDelegate = ( ( PostInsertIdentifierGenerator ) getIdentifierGenerator() )
.getInsertGeneratedIdentifierDelegate( this, getFactory().getDialect(), useGetGeneratedKeys() );
sqlIdentityInsertString = customSQLInsert[0] == null
? generateIdentityInsertString( getPropertyInsertability() )
: customSQLInsert[0];
}
else {
sqlIdentityInsertString = null;
}
logStaticSQL();
}
public final void postInstantiate() throws MappingException {
doLateInit();
createLoaders();
createUniqueKeyLoaders();
createQueryLoader();
doPostInstantiate();
}
protected void doPostInstantiate() {
}
//needed by subclasses to override the createLoader strategy
protected Map getLoaders() {
return loaders;
}
//Relational based Persisters should be content with this implementation
protected void createLoaders() {
final Map loaders = getLoaders();
loaders.put( LockMode.NONE, createEntityLoader( LockMode.NONE ) );
UniqueEntityLoader readLoader = createEntityLoader( LockMode.READ );
loaders.put( LockMode.READ, readLoader );
//TODO: inexact, what we really need to know is: are any outer joins used?
boolean disableForUpdate = getSubclassTableSpan() > 1 &&
hasSubclasses() &&
!getFactory().getDialect().supportsOuterJoinForUpdate();
loaders.put(
LockMode.UPGRADE,
disableForUpdate ?
readLoader :
createEntityLoader( LockMode.UPGRADE )
);
loaders.put(
LockMode.UPGRADE_NOWAIT,
disableForUpdate ?
readLoader :
createEntityLoader( LockMode.UPGRADE_NOWAIT )
);
loaders.put(
LockMode.UPGRADE_SKIPLOCKED,
disableForUpdate ?
readLoader :
createEntityLoader( LockMode.UPGRADE_SKIPLOCKED )
);
loaders.put(
LockMode.FORCE,
disableForUpdate ?
readLoader :
createEntityLoader( LockMode.FORCE )
);
loaders.put(
LockMode.PESSIMISTIC_READ,
disableForUpdate ?
readLoader :
createEntityLoader( LockMode.PESSIMISTIC_READ )
);
loaders.put(
LockMode.PESSIMISTIC_WRITE,
disableForUpdate ?
readLoader :
createEntityLoader( LockMode.PESSIMISTIC_WRITE )
);
loaders.put(
LockMode.PESSIMISTIC_FORCE_INCREMENT,
disableForUpdate ?
readLoader :
createEntityLoader( LockMode.PESSIMISTIC_FORCE_INCREMENT )
);
loaders.put( LockMode.OPTIMISTIC, createEntityLoader( LockMode.OPTIMISTIC) );
loaders.put( LockMode.OPTIMISTIC_FORCE_INCREMENT, createEntityLoader(LockMode.OPTIMISTIC_FORCE_INCREMENT) );
loaders.put(
"merge",
new CascadeEntityLoader( this, CascadingActions.MERGE, getFactory() )
);
loaders.put(
"refresh",
new CascadeEntityLoader( this, CascadingActions.REFRESH, getFactory() )
);
}
protected void createQueryLoader() {
if ( loaderName != null ) {
queryLoader = new NamedQueryLoader( loaderName, this );
}
}
/**
* Load an instance using either the forUpdateLoader or the outer joining loader ,
* depending upon the value of the lock parameter
*/
public Object load(Serializable id, Object optionalObject, LockMode lockMode, SessionImplementor session) {
return load( id, optionalObject, new LockOptions().setLockMode(lockMode), session );
}
/**
* Load an instance using either the forUpdateLoader or the outer joining loader ,
* depending upon the value of the lock parameter
*/
public Object load(Serializable id, Object optionalObject, LockOptions lockOptions, SessionImplementor session)
throws HibernateException {
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Fetching entity: {0}", MessageHelper.infoString( this, id, getFactory() ) );
}
final UniqueEntityLoader loader = getAppropriateLoader(lockOptions, session );
return loader.load( id, optionalObject, session, lockOptions );
}
public void registerAffectingFetchProfile(String fetchProfileName) {
affectingFetchProfileNames.add( fetchProfileName );
}
private boolean isAffectedByEntityGraph(SessionImplementor session) {
return session.getLoadQueryInfluencers().getFetchGraph() != null || session.getLoadQueryInfluencers()
.getLoadGraph() != null;
}
private boolean isAffectedByEnabledFetchProfiles(SessionImplementor session) {
for ( String s : session.getLoadQueryInfluencers().getEnabledFetchProfileNames() ) {
if ( affectingFetchProfileNames.contains( s ) ) {
return true;
}
}
return false;
}
private boolean isAffectedByEnabledFilters(SessionImplementor session) {
return session.getLoadQueryInfluencers().hasEnabledFilters()
&& filterHelper.isAffectedBy( session.getLoadQueryInfluencers().getEnabledFilters() );
}
private UniqueEntityLoader getAppropriateLoader(LockOptions lockOptions, SessionImplementor session) {
if ( queryLoader != null ) {
// if the user specified a custom query loader we need to that
// regardless of any other consideration
return queryLoader;
}
else if ( isAffectedByEnabledFilters( session ) ) {
// because filters affect the rows returned (because they add
// restrictions) these need to be next in precedence
return createEntityLoader(lockOptions, session.getLoadQueryInfluencers() );
}
else if ( session.getLoadQueryInfluencers().getInternalFetchProfile() != null && LockMode.UPGRADE.greaterThan( lockOptions.getLockMode() ) ) {
// Next, we consider whether an 'internal' fetch profile has been set.
// This indicates a special fetch profile Hibernate needs applied
// (for its merge loading process e.g.).
return ( UniqueEntityLoader ) getLoaders().get( session.getLoadQueryInfluencers().getInternalFetchProfile() );
}
else if ( isAffectedByEnabledFetchProfiles( session ) ) {
// If the session has associated influencers we need to adjust the
// SQL query used for loading based on those influencers
return createEntityLoader(lockOptions, session.getLoadQueryInfluencers() );
}
else if ( isAffectedByEntityGraph( session ) ) {
return createEntityLoader( lockOptions, session.getLoadQueryInfluencers() );
}
else if ( lockOptions.getTimeOut() != LockOptions.WAIT_FOREVER ) {
return createEntityLoader( lockOptions, session.getLoadQueryInfluencers() );
}
else {
return ( UniqueEntityLoader ) getLoaders().get( lockOptions.getLockMode() );
}
}
private boolean isAllNull(Object[] array, int tableNumber) {
for ( int i = 0; i < array.length; i++ ) {
if ( isPropertyOfTable( i, tableNumber ) && array[i] != null ) {
return false;
}
}
return true;
}
public boolean isSubclassPropertyNullable(int i) {
return subclassPropertyNullabilityClosure[i];
}
/**
* Transform the array of property indexes to an array of booleans,
* true when the property is dirty
*/
protected final boolean[] getPropertiesToUpdate(final int[] dirtyProperties, final boolean hasDirtyCollection) {
final boolean[] propsToUpdate = new boolean[ entityMetamodel.getPropertySpan() ];
final boolean[] updateability = getPropertyUpdateability(); //no need to check laziness, dirty checking handles that
for ( int j = 0; j < dirtyProperties.length; j++ ) {
int property = dirtyProperties[j];
if ( updateability[property] ) {
propsToUpdate[property] = true;
}
}
if ( isVersioned() && updateability[getVersionProperty() ]) {
propsToUpdate[ getVersionProperty() ] =
Versioning.isVersionIncrementRequired( dirtyProperties, hasDirtyCollection, getPropertyVersionability() );
}
return propsToUpdate;
}
/**
* Transform the array of property indexes to an array of booleans,
* true when the property is insertable and non-null
*/
protected boolean[] getPropertiesToInsert(Object[] fields) {
boolean[] notNull = new boolean[fields.length];
boolean[] insertable = getPropertyInsertability();
for ( int i = 0; i < fields.length; i++ ) {
notNull[i] = insertable[i] && fields[i] != null;
}
return notNull;
}
/**
* Locate the property-indices of all properties considered to be dirty.
*
* @param currentState The current state of the entity (the state to be checked).
* @param previousState The previous state of the entity (the state to be checked against).
* @param entity The entity for which we are checking state dirtiness.
* @param session The session in which the check is occurring.
* @return null or the indices of the dirty properties
* @throws HibernateException
*/
public int[] findDirty(Object[] currentState, Object[] previousState, Object entity, SessionImplementor session)
throws HibernateException {
int[] props = TypeHelper.findDirty(
entityMetamodel.getProperties(),
currentState,
previousState,
propertyColumnUpdateable,
hasUninitializedLazyProperties( entity ),
session
);
if ( props == null ) {
return null;
}
else {
logDirtyProperties( props );
return props;
}
}
/**
* Locate the property-indices of all properties considered to be dirty.
*
* @param old The old state of the entity.
* @param current The current state of the entity.
* @param entity The entity for which we are checking state modification.
* @param session The session in which the check is occurring.
* @return null or the indices of the modified properties
* @throws HibernateException
*/
public int[] findModified(Object[] old, Object[] current, Object entity, SessionImplementor session)
throws HibernateException {
int[] props = TypeHelper.findModified(
entityMetamodel.getProperties(),
current,
old,
propertyColumnUpdateable,
hasUninitializedLazyProperties( entity ),
session
);
if ( props == null ) {
return null;
}
else {
logDirtyProperties( props );
return props;
}
}
/**
* Which properties appear in the SQL update?
* (Initialized, updateable ones!)
*/
protected boolean[] getPropertyUpdateability(Object entity) {
return hasUninitializedLazyProperties( entity )
? getNonLazyPropertyUpdateability()
: getPropertyUpdateability();
}
private void logDirtyProperties(int[] props) {
if ( LOG.isTraceEnabled() ) {
for ( int i = 0; i < props.length; i++ ) {
String propertyName = entityMetamodel.getProperties()[ props[i] ].getName();
LOG.trace( StringHelper.qualify( getEntityName(), propertyName ) + " is dirty" );
}
}
}
public SessionFactoryImplementor getFactory() {
return factory;
}
public EntityMetamodel getEntityMetamodel() {
return entityMetamodel;
}
public boolean hasCache() {
return cacheAccessStrategy != null;
}
public EntityRegionAccessStrategy getCacheAccessStrategy() {
return cacheAccessStrategy;
}
@Override
public CacheEntryStructure getCacheEntryStructure() {
return cacheEntryHelper.getCacheEntryStructure();
}
@Override
public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session) {
return cacheEntryHelper.buildCacheEntry( entity, state, version, session );
}
public boolean hasNaturalIdCache() {
return naturalIdRegionAccessStrategy != null;
}
public NaturalIdRegionAccessStrategy getNaturalIdCacheAccessStrategy() {
return naturalIdRegionAccessStrategy;
}
public Comparator getVersionComparator() {
return isVersioned() ? getVersionType().getComparator() : null;
}
// temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public final String getEntityName() {
return entityMetamodel.getName();
}
public EntityType getEntityType() {
return entityMetamodel.getEntityType();
}
public boolean isPolymorphic() {
return entityMetamodel.isPolymorphic();
}
public boolean isInherited() {
return entityMetamodel.isInherited();
}
public boolean hasCascades() {
return entityMetamodel.hasCascades();
}
public boolean hasIdentifierProperty() {
return !entityMetamodel.getIdentifierProperty().isVirtual();
}
public VersionType getVersionType() {
return ( VersionType ) locateVersionType();
}
private Type locateVersionType() {
return entityMetamodel.getVersionProperty() == null ?
null :
entityMetamodel.getVersionProperty().getType();
}
public int getVersionProperty() {
return entityMetamodel.getVersionPropertyIndex();
}
public boolean isVersioned() {
return entityMetamodel.isVersioned();
}
public boolean isIdentifierAssignedByInsert() {
return entityMetamodel.getIdentifierProperty().isIdentifierAssignedByInsert();
}
public boolean hasLazyProperties() {
return entityMetamodel.hasLazyProperties();
}
// public boolean hasUninitializedLazyProperties(Object entity) {
// if ( hasLazyProperties() ) {
// InterceptFieldCallback callback = ( ( InterceptFieldEnabled ) entity ).getInterceptFieldCallback();
// return callback != null && !( ( FieldInterceptor ) callback ).isInitialized();
// }
// else {
// return false;
// }
// }
public void afterReassociate(Object entity, SessionImplementor session) {
if ( getEntityMetamodel().getInstrumentationMetadata().isInstrumented() ) {
FieldInterceptor interceptor = getEntityMetamodel().getInstrumentationMetadata().extractInterceptor( entity );
if ( interceptor != null ) {
interceptor.setSession( session );
}
else {
FieldInterceptor fieldInterceptor = getEntityMetamodel().getInstrumentationMetadata().injectInterceptor(
entity,
getEntityName(),
null,
session
);
fieldInterceptor.dirty();
}
}
handleNaturalIdReattachment( entity, session );
}
private void handleNaturalIdReattachment(Object entity, SessionImplementor session) {
if ( ! hasNaturalIdentifier() ) {
return;
}
if ( getEntityMetamodel().hasImmutableNaturalId() ) {
// we assume there were no changes to natural id during detachment for now, that is validated later
// during flush.
return;
}
final NaturalIdHelper naturalIdHelper = session.getPersistenceContext().getNaturalIdHelper();
final Serializable id = getIdentifier( entity, session );
// for reattachment of mutable natural-ids, we absolutely positively have to grab the snapshot from the
// database, because we have no other way to know if the state changed while detached.
final Object[] naturalIdSnapshot;
final Object[] entitySnapshot = session.getPersistenceContext().getDatabaseSnapshot( id, this );
if ( entitySnapshot == StatefulPersistenceContext.NO_ROW ) {
naturalIdSnapshot = null;
}
else {
naturalIdSnapshot = naturalIdHelper.extractNaturalIdValues( entitySnapshot, this );
}
naturalIdHelper.removeSharedNaturalIdCrossReference( this, id, naturalIdSnapshot );
naturalIdHelper.manageLocalNaturalIdCrossReference(
this,
id,
naturalIdHelper.extractNaturalIdValues( entity, this ),
naturalIdSnapshot,
CachedNaturalIdValueSource.UPDATE
);
}
public Boolean isTransient(Object entity, SessionImplementor session) throws HibernateException {
final Serializable id;
if ( canExtractIdOutOfEntity() ) {
id = getIdentifier( entity, session );
}
else {
id = null;
}
// we *always* assume an instance with a null
// identifier or no identifier property is unsaved!
if ( id == null ) {
return Boolean.TRUE;
}
// check the version unsaved-value, if appropriate
final Object version = getVersion( entity );
if ( isVersioned() ) {
// let this take precedence if defined, since it works for
// assigned identifiers
Boolean result = entityMetamodel.getVersionProperty()
.getUnsavedValue().isUnsaved( version );
if ( result != null ) {
return result;
}
}
// check the id unsaved-value
Boolean result = entityMetamodel.getIdentifierProperty()
.getUnsavedValue().isUnsaved( id );
if ( result != null ) {
return result;
}
// check to see if it is in the second-level cache
if ( session.getCacheMode().isGetEnabled() && hasCache() ) {
final CacheKey ck = session.generateCacheKey( id, getIdentifierType(), getRootEntityName() );
final Object ce = CacheHelper.fromSharedCache( session, ck, getCacheAccessStrategy() );
if ( ce != null ) {
return Boolean.FALSE;
}
}
return null;
}
public boolean hasCollections() {
return entityMetamodel.hasCollections();
}
public boolean hasMutableProperties() {
return entityMetamodel.hasMutableProperties();
}
public boolean isMutable() {
return entityMetamodel.isMutable();
}
private boolean isModifiableEntity(EntityEntry entry) {
return ( entry == null ? isMutable() : entry.isModifiableEntity() );
}
public boolean isAbstract() {
return entityMetamodel.isAbstract();
}
public boolean hasSubclasses() {
return entityMetamodel.hasSubclasses();
}
public boolean hasProxy() {
return entityMetamodel.isLazy();
}
public IdentifierGenerator getIdentifierGenerator() throws HibernateException {
return entityMetamodel.getIdentifierProperty().getIdentifierGenerator();
}
public String getRootEntityName() {
return entityMetamodel.getRootName();
}
public ClassMetadata getClassMetadata() {
return this;
}
public String getMappedSuperclass() {
return entityMetamodel.getSuperclass();
}
public boolean isExplicitPolymorphism() {
return entityMetamodel.isExplicitPolymorphism();
}
protected boolean useDynamicUpdate() {
return entityMetamodel.isDynamicUpdate();
}
protected boolean useDynamicInsert() {
return entityMetamodel.isDynamicInsert();
}
protected boolean hasEmbeddedCompositeIdentifier() {
return entityMetamodel.getIdentifierProperty().isEmbedded();
}
public boolean canExtractIdOutOfEntity() {
return hasIdentifierProperty() || hasEmbeddedCompositeIdentifier() || hasIdentifierMapper();
}
private boolean hasIdentifierMapper() {
return entityMetamodel.getIdentifierProperty().hasIdentifierMapper();
}
public String[] getKeyColumnNames() {
return getIdentifierColumnNames();
}
public String getName() {
return getEntityName();
}
public boolean isCollection() {
return false;
}
public boolean consumesEntityAlias() {
return true;
}
public boolean consumesCollectionAlias() {
return false;
}
public Type getPropertyType(String propertyName) throws MappingException {
return propertyMapping.toType( propertyName );
}
public Type getType() {
return entityMetamodel.getEntityType();
}
public boolean isSelectBeforeUpdateRequired() {
return entityMetamodel.isSelectBeforeUpdate();
}
protected final OptimisticLockStyle optimisticLockStyle() {
return entityMetamodel.getOptimisticLockStyle();
}
public Object createProxy(Serializable id, SessionImplementor session) throws HibernateException {
return entityMetamodel.getTuplizer().createProxy( id, session );
}
public String toString() {
return StringHelper.unqualify( getClass().getName() ) +
'(' + entityMetamodel.getName() + ')';
}
public final String selectFragment(
Joinable rhs,
String rhsAlias,
String lhsAlias,
String entitySuffix,
String collectionSuffix,
boolean includeCollectionColumns) {
return selectFragment( lhsAlias, entitySuffix );
}
public boolean isInstrumented() {
return entityMetamodel.isInstrumented();
}
public boolean hasInsertGeneratedProperties() {
return entityMetamodel.hasInsertGeneratedValues();
}
public boolean hasUpdateGeneratedProperties() {
return entityMetamodel.hasUpdateGeneratedValues();
}
public boolean isVersionPropertyGenerated() {
return isVersioned() && getEntityMetamodel().isVersionGenerated();
}
public boolean isVersionPropertyInsertable() {
return isVersioned() && getPropertyInsertability() [ getVersionProperty() ];
}
public void afterInitialize(Object entity, boolean lazyPropertiesAreUnfetched, SessionImplementor session) {
getEntityTuplizer().afterInitialize( entity, lazyPropertiesAreUnfetched, session );
}
public String[] getPropertyNames() {
return entityMetamodel.getPropertyNames();
}
public Type[] getPropertyTypes() {
return entityMetamodel.getPropertyTypes();
}
public boolean[] getPropertyLaziness() {
return entityMetamodel.getPropertyLaziness();
}
public boolean[] getPropertyUpdateability() {
return entityMetamodel.getPropertyUpdateability();
}
public boolean[] getPropertyCheckability() {
return entityMetamodel.getPropertyCheckability();
}
public boolean[] getNonLazyPropertyUpdateability() {
return entityMetamodel.getNonlazyPropertyUpdateability();
}
public boolean[] getPropertyInsertability() {
return entityMetamodel.getPropertyInsertability();
}
@Deprecated
public ValueInclusion[] getPropertyInsertGenerationInclusions() {
return null;
}
@Deprecated
public ValueInclusion[] getPropertyUpdateGenerationInclusions() {
return null;
}
public boolean[] getPropertyNullability() {
return entityMetamodel.getPropertyNullability();
}
public boolean[] getPropertyVersionability() {
return entityMetamodel.getPropertyVersionability();
}
public CascadeStyle[] getPropertyCascadeStyles() {
return entityMetamodel.getCascadeStyles();
}
public final Class getMappedClass() {
return getEntityTuplizer().getMappedClass();
}
public boolean implementsLifecycle() {
return getEntityTuplizer().isLifecycleImplementor();
}
public Class getConcreteProxyClass() {
return getEntityTuplizer().getConcreteProxyClass();
}
public void setPropertyValues(Object object, Object[] values) {
getEntityTuplizer().setPropertyValues( object, values );
}
public void setPropertyValue(Object object, int i, Object value) {
getEntityTuplizer().setPropertyValue( object, i, value );
}
public Object[] getPropertyValues(Object object) {
return getEntityTuplizer().getPropertyValues( object );
}
@Override
public Object getPropertyValue(Object object, int i) {
return getEntityTuplizer().getPropertyValue( object, i );
}
@Override
public Object getPropertyValue(Object object, String propertyName) {
return getEntityTuplizer().getPropertyValue( object, propertyName );
}
@Override
public Serializable getIdentifier(Object object) {
return getEntityTuplizer().getIdentifier( object, null );
}
@Override
public Serializable getIdentifier(Object entity, SessionImplementor session) {
return getEntityTuplizer().getIdentifier( entity, session );
}
@Override
public void setIdentifier(Object entity, Serializable id, SessionImplementor session) {
getEntityTuplizer().setIdentifier( entity, id, session );
}
@Override
public Object getVersion(Object object) {
return getEntityTuplizer().getVersion( object );
}
@Override
public Object instantiate(Serializable id, SessionImplementor session) {
return getEntityTuplizer().instantiate( id, session );
}
@Override
public boolean isInstance(Object object) {
return getEntityTuplizer().isInstance( object );
}
@Override
public boolean hasUninitializedLazyProperties(Object object) {
return getEntityTuplizer().hasUninitializedLazyProperties( object );
}
@Override
public void resetIdentifier(Object entity, Serializable currentId, Object currentVersion, SessionImplementor session) {
getEntityTuplizer().resetIdentifier( entity, currentId, currentVersion, session );
}
@Override
public EntityPersister getSubclassEntityPersister(Object instance, SessionFactoryImplementor factory) {
if ( !hasSubclasses() ) {
return this;
}
else {
final String concreteEntityName = getEntityTuplizer().determineConcreteSubclassEntityName(
instance,
factory
);
if ( concreteEntityName == null || getEntityName().equals( concreteEntityName ) ) {
// the contract of EntityTuplizer.determineConcreteSubclassEntityName says that returning null
// is an indication that the specified entity-name (this.getEntityName) should be used.
return this;
}
else {
return factory.getEntityPersister( concreteEntityName );
}
}
}
public boolean isMultiTable() {
return false;
}
public String getTemporaryIdTableName() {
return temporaryIdTableName;
}
public String getTemporaryIdTableDDL() {
return temporaryIdTableDDL;
}
protected int getPropertySpan() {
return entityMetamodel.getPropertySpan();
}
public Object[] getPropertyValuesToInsert(Object object, Map mergeMap, SessionImplementor session) throws HibernateException {
return getEntityTuplizer().getPropertyValuesToInsert( object, mergeMap, session );
}
public void processInsertGeneratedProperties(Serializable id, Object entity, Object[] state, SessionImplementor session) {
if ( !hasInsertGeneratedProperties() ) {
throw new AssertionFailure("no insert-generated properties");
}
processGeneratedProperties( id, entity, state, session, sqlInsertGeneratedValuesSelectString, GenerationTiming.INSERT );
}
public void processUpdateGeneratedProperties(Serializable id, Object entity, Object[] state, SessionImplementor session) {
if ( !hasUpdateGeneratedProperties() ) {
throw new AssertionFailure("no update-generated properties");
}
processGeneratedProperties( id, entity, state, session, sqlUpdateGeneratedValuesSelectString, GenerationTiming.ALWAYS );
}
private void processGeneratedProperties(
Serializable id,
Object entity,
Object[] state,
SessionImplementor session,
String selectionSQL,
GenerationTiming matchTiming) {
// force immediate execution of the insert batch (if one)
session.getTransactionCoordinator().getJdbcCoordinator().executeBatch();
try {
PreparedStatement ps = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( selectionSQL );
try {
getIdentifierType().nullSafeSet( ps, id, 1, session );
ResultSet rs = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().extract( ps );
try {
if ( !rs.next() ) {
throw new HibernateException(
"Unable to locate row for retrieval of generated properties: " +
MessageHelper.infoString( this, id, getFactory() )
);
}
int propertyIndex = -1;
for ( NonIdentifierAttribute attribute : entityMetamodel.getProperties() ) {
propertyIndex++;
final ValueGeneration valueGeneration = attribute.getValueGenerationStrategy();
if ( isReadRequired( valueGeneration, matchTiming ) ) {
final Object hydratedState = attribute.getType().hydrate(
rs, getPropertyAliases(
"",
propertyIndex
), session, entity
);
state[propertyIndex] = attribute.getType().resolve( hydratedState, session, entity );
setPropertyValue( entity, propertyIndex, state[propertyIndex] );
}
}
// for ( int i = 0; i < getPropertySpan(); i++ ) {
// if ( includeds[i] != ValueInclusion.NONE ) {
// Object hydratedState = getPropertyTypes()[i].hydrate( rs, getPropertyAliases( "", i ), session, entity );
// state[i] = getPropertyTypes()[i].resolve( hydratedState, session, entity );
// setPropertyValue( entity, i, state[i] );
// }
// }
}
finally {
if ( rs != null ) {
session.getTransactionCoordinator().getJdbcCoordinator().release( rs, ps );
}
}
}
finally {
session.getTransactionCoordinator().getJdbcCoordinator().release( ps );
}
}
catch( SQLException e ) {
throw getFactory().getSQLExceptionHelper().convert(
e,
"unable to select generated column values",
selectionSQL
);
}
}
/**
* Whether the given value generation strategy requires to read the value from the database or not.
*/
private boolean isReadRequired(ValueGeneration valueGeneration, GenerationTiming matchTiming) {
return valueGeneration != null &&
valueGeneration.getValueGenerator() == null &&
timingsMatch( valueGeneration.getGenerationTiming(), matchTiming );
}
private boolean timingsMatch(GenerationTiming timing, GenerationTiming matchTiming) {
return
(matchTiming == GenerationTiming.INSERT && timing.includesInsert()) ||
(matchTiming == GenerationTiming.ALWAYS && timing.includesUpdate());
}
public String getIdentifierPropertyName() {
return entityMetamodel.getIdentifierProperty().getName();
}
public Type getIdentifierType() {
return entityMetamodel.getIdentifierProperty().getType();
}
public boolean hasSubselectLoadableCollections() {
return hasSubselectLoadableCollections;
}
public int[] getNaturalIdentifierProperties() {
return entityMetamodel.getNaturalIdentifierProperties();
}
public Object[] getNaturalIdentifierSnapshot(Serializable id, SessionImplementor session) throws HibernateException {
if ( !hasNaturalIdentifier() ) {
throw new MappingException( "persistent class did not define a natural-id : " + MessageHelper.infoString( this ) );
}
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Getting current natural-id snapshot state for: {0}",
MessageHelper.infoString( this, id, getFactory() ) );
}
int[] naturalIdPropertyIndexes = getNaturalIdentifierProperties();
int naturalIdPropertyCount = naturalIdPropertyIndexes.length;
boolean[] naturalIdMarkers = new boolean[ getPropertySpan() ];
Type[] extractionTypes = new Type[ naturalIdPropertyCount ];
for ( int i = 0; i < naturalIdPropertyCount; i++ ) {
extractionTypes[i] = getPropertyTypes()[ naturalIdPropertyIndexes[i] ];
naturalIdMarkers[ naturalIdPropertyIndexes[i] ] = true;
}
///////////////////////////////////////////////////////////////////////
// TODO : look at perhaps caching this...
Select select = new Select( getFactory().getDialect() );
if ( getFactory().getSettings().isCommentsEnabled() ) {
select.setComment( "get current natural-id state " + getEntityName() );
}
select.setSelectClause( concretePropertySelectFragmentSansLeadingComma( getRootAlias(), naturalIdMarkers ) );
select.setFromClause( fromTableFragment( getRootAlias() ) + fromJoinFragment( getRootAlias(), true, false ) );
String[] aliasedIdColumns = StringHelper.qualify( getRootAlias(), getIdentifierColumnNames() );
String whereClause = new StringBuilder()
.append( StringHelper.join( "=? and ",
aliasedIdColumns ) )
.append( "=?" )
.append( whereJoinFragment( getRootAlias(), true, false ) )
.toString();
String sql = select.setOuterJoins( "", "" )
.setWhereClause( whereClause )
.toStatementString();
///////////////////////////////////////////////////////////////////////
Object[] snapshot = new Object[ naturalIdPropertyCount ];
try {
PreparedStatement ps = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql );
try {
getIdentifierType().nullSafeSet( ps, id, 1, session );
ResultSet rs = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().extract( ps );
try {
//if there is no resulting row, return null
if ( !rs.next() ) {
return null;
}
final EntityKey key = session.generateEntityKey( id, this );
Object owner = session.getPersistenceContext().getEntity( key );
for ( int i = 0; i < naturalIdPropertyCount; i++ ) {
snapshot[i] = extractionTypes[i].hydrate( rs, getPropertyAliases( "", naturalIdPropertyIndexes[i] ), session, null );
if (extractionTypes[i].isEntityType()) {
snapshot[i] = extractionTypes[i].resolve(snapshot[i], session, owner);
}
}
return snapshot;
}
finally {
session.getTransactionCoordinator().getJdbcCoordinator().release( rs, ps );
}
}
finally {
session.getTransactionCoordinator().getJdbcCoordinator().release( ps );
}
}
catch ( SQLException e ) {
throw getFactory().getSQLExceptionHelper().convert(
e,
"could not retrieve snapshot: " + MessageHelper.infoString( this, id, getFactory() ),
sql
);
}
}
@Override
public Serializable loadEntityIdByNaturalId(
Object[] naturalIdValues,
LockOptions lockOptions,
SessionImplementor session) {
if ( LOG.isTraceEnabled() ) {
LOG.tracef(
"Resolving natural-id [%s] to id : %s ",
naturalIdValues,
MessageHelper.infoString( this )
);
}
final boolean[] valueNullness = determineValueNullness( naturalIdValues );
final String sqlEntityIdByNaturalIdString = determinePkByNaturalIdQuery( valueNullness );
try {
PreparedStatement ps = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sqlEntityIdByNaturalIdString );
try {
int positions = 1;
int loop = 0;
for ( int idPosition : getNaturalIdentifierProperties() ) {
final Object naturalIdValue = naturalIdValues[loop++];
if ( naturalIdValue != null ) {
final Type type = getPropertyTypes()[idPosition];
type.nullSafeSet( ps, naturalIdValue, positions, session );
positions += type.getColumnSpan( session.getFactory() );
}
}
ResultSet rs = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().extract( ps );
try {
// if there is no resulting row, return null
if ( !rs.next() ) {
return null;
}
return (Serializable) getIdentifierType().hydrate( rs, getIdentifierAliases(), session, null );
}
finally {
session.getTransactionCoordinator().getJdbcCoordinator().release( rs, ps );
}
}
finally {
session.getTransactionCoordinator().getJdbcCoordinator().release( ps );
}
}
catch ( SQLException e ) {
throw getFactory().getSQLExceptionHelper().convert(
e,
String.format(
"could not resolve natural-id [%s] to id : %s",
naturalIdValues,
MessageHelper.infoString( this )
),
sqlEntityIdByNaturalIdString
);
}
}
private boolean[] determineValueNullness(Object[] naturalIdValues) {
boolean[] nullness = new boolean[ naturalIdValues.length ];
for ( int i = 0; i < naturalIdValues.length; i++ ) {
nullness[i] = naturalIdValues[i] == null;
}
return nullness;
}
private Boolean naturalIdIsNonNullable;
private String cachedPkByNonNullableNaturalIdQuery;
private String determinePkByNaturalIdQuery(boolean[] valueNullness) {
if ( ! hasNaturalIdentifier() ) {
throw new HibernateException( "Attempt to build natural-id -> PK resolution query for entity that does not define natural id" );
}
// performance shortcut for cases where the natural-id is defined as completely non-nullable
if ( isNaturalIdNonNullable() ) {
if ( valueNullness != null && ! ArrayHelper.isAllFalse( valueNullness ) ) {
throw new HibernateException( "Null value(s) passed to lookup by non-nullable natural-id" );
}
if ( cachedPkByNonNullableNaturalIdQuery == null ) {
cachedPkByNonNullableNaturalIdQuery = generateEntityIdByNaturalIdSql( null );
}
return cachedPkByNonNullableNaturalIdQuery;
}
// Otherwise, regenerate it each time
return generateEntityIdByNaturalIdSql( valueNullness );
}
protected boolean isNaturalIdNonNullable() {
if ( naturalIdIsNonNullable == null ) {
naturalIdIsNonNullable = determineNaturalIdNullability();
}
return naturalIdIsNonNullable;
}
private boolean determineNaturalIdNullability() {
boolean[] nullability = getPropertyNullability();
for ( int position : getNaturalIdentifierProperties() ) {
// if any individual property is nullable, return false
if ( nullability[position] ) {
return false;
}
}
// return true if we found no individually nullable properties
return true;
}
private String generateEntityIdByNaturalIdSql(boolean[] valueNullness) {
EntityPersister rootPersister = getFactory().getEntityPersister( getRootEntityName() );
if ( rootPersister != this ) {
if ( rootPersister instanceof AbstractEntityPersister ) {
return ( (AbstractEntityPersister) rootPersister ).generateEntityIdByNaturalIdSql( valueNullness );
}
}
Select select = new Select( getFactory().getDialect() );
if ( getFactory().getSettings().isCommentsEnabled() ) {
select.setComment( "get current natural-id->entity-id state " + getEntityName() );
}
final String rootAlias = getRootAlias();
select.setSelectClause( identifierSelectFragment( rootAlias, "" ) );
select.setFromClause( fromTableFragment( rootAlias ) + fromJoinFragment( rootAlias, true, false ) );
final StringBuilder whereClause = new StringBuilder();
final int[] propertyTableNumbers = getPropertyTableNumbers();
final int[] naturalIdPropertyIndexes = this.getNaturalIdentifierProperties();
int valuesIndex = -1;
for ( int propIdx = 0; propIdx < naturalIdPropertyIndexes.length; propIdx++ ) {
valuesIndex++;
if ( propIdx > 0 ) {
whereClause.append( " and " );
}
final int naturalIdIdx = naturalIdPropertyIndexes[propIdx];
final String tableAlias = generateTableAlias( rootAlias, propertyTableNumbers[naturalIdIdx] );
final String[] propertyColumnNames = getPropertyColumnNames( naturalIdIdx );
final String[] aliasedPropertyColumns = StringHelper.qualify( tableAlias, propertyColumnNames );
if ( valueNullness != null && valueNullness[valuesIndex] ) {
whereClause.append( StringHelper.join( " is null and ", aliasedPropertyColumns ) ).append( " is null" );
}
else {
whereClause.append( StringHelper.join( "=? and ", aliasedPropertyColumns ) ).append( "=?" );
}
}
whereClause.append( whereJoinFragment( getRootAlias(), true, false ) );
return select.setOuterJoins( "", "" ).setWhereClause( whereClause.toString() ).toStatementString();
}
protected String concretePropertySelectFragmentSansLeadingComma(String alias, boolean[] include) {
String concretePropertySelectFragment = concretePropertySelectFragment( alias, include );
int firstComma = concretePropertySelectFragment.indexOf( ", " );
if ( firstComma == 0 ) {
concretePropertySelectFragment = concretePropertySelectFragment.substring( 2 );
}
return concretePropertySelectFragment;
}
public boolean hasNaturalIdentifier() {
return entityMetamodel.hasNaturalIdentifier();
}
public void setPropertyValue(Object object, String propertyName, Object value) {
getEntityTuplizer().setPropertyValue( object, propertyName, value );
}
public static int getTableId(String tableName, String[] tables) {
for ( int j = 0; j < tables.length; j++ ) {
if ( tableName.equalsIgnoreCase( tables[j] ) ) {
return j;
}
}
throw new AssertionFailure( "Table " + tableName + " not found" );
}
@Override
public EntityMode getEntityMode() {
return entityMetamodel.getEntityMode();
}
@Override
public EntityTuplizer getEntityTuplizer() {
return entityTuplizer;
}
@Override
public EntityInstrumentationMetadata getInstrumentationMetadata() {
return entityMetamodel.getInstrumentationMetadata();
}
@Override
public String getTableAliasForColumn(String columnName, String rootAlias) {
return generateTableAlias( rootAlias, determineTableNumberForColumn( columnName ) );
}
public int determineTableNumberForColumn(String columnName) {
return 0;
}
/**
* Consolidated these onto a single helper because the 2 pieces work in tandem.
*/
public static interface CacheEntryHelper {
public CacheEntryStructure getCacheEntryStructure();
public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session);
}
private static class StandardCacheEntryHelper implements CacheEntryHelper {
private final EntityPersister persister;
private StandardCacheEntryHelper(EntityPersister persister) {
this.persister = persister;
}
@Override
public CacheEntryStructure getCacheEntryStructure() {
return UnstructuredCacheEntry.INSTANCE;
}
@Override
public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session) {
return new StandardCacheEntryImpl(
state,
persister,
persister.hasUninitializedLazyProperties( entity ),
version,
session,
entity
);
}
}
private static class ReferenceCacheEntryHelper implements CacheEntryHelper {
private final EntityPersister persister;
private ReferenceCacheEntryHelper(EntityPersister persister) {
this.persister = persister;
}
@Override
public CacheEntryStructure getCacheEntryStructure() {
return UnstructuredCacheEntry.INSTANCE;
}
@Override
public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session) {
return new ReferenceCacheEntryImpl( entity, persister );
}
}
private static class StructuredCacheEntryHelper implements CacheEntryHelper {
private final EntityPersister persister;
private final StructuredCacheEntry structure;
private StructuredCacheEntryHelper(EntityPersister persister) {
this.persister = persister;
this.structure = new StructuredCacheEntry( persister );
}
@Override
public CacheEntryStructure getCacheEntryStructure() {
return structure;
}
@Override
public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session) {
return new StandardCacheEntryImpl(
state,
persister,
persister.hasUninitializedLazyProperties( entity ),
version,
session,
entity
);
}
}
private static class NoopCacheEntryHelper implements CacheEntryHelper {
public static final NoopCacheEntryHelper INSTANCE = new NoopCacheEntryHelper();
@Override
public CacheEntryStructure getCacheEntryStructure() {
return UnstructuredCacheEntry.INSTANCE;
}
@Override
public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session) {
throw new HibernateException( "Illegal attempt to build cache entry for non-cached entity" );
}
}
// EntityDefinition impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private EntityIdentifierDefinition entityIdentifierDefinition;
private Iterable embeddedCompositeIdentifierAttributes;
private Iterable attributeDefinitions;
@Override
public void generateEntityDefinition() {
prepareEntityIdentifierDefinition();
collectAttributeDefinitions();
}
@Override
public EntityPersister getEntityPersister() {
return this;
}
@Override
public EntityIdentifierDefinition getEntityKeyDefinition() {
return entityIdentifierDefinition;
}
@Override
public Iterable getAttributes() {
return attributeDefinitions;
}
private void prepareEntityIdentifierDefinition() {
if ( entityIdentifierDefinition != null ) {
return;
}
final Type idType = getIdentifierType();
if ( !idType.isComponentType() ) {
entityIdentifierDefinition =
EntityIdentifierDefinitionHelper.buildSimpleEncapsulatedIdentifierDefinition( this );
return;
}
final CompositeType cidType = (CompositeType) idType;
if ( !cidType.isEmbedded() ) {
entityIdentifierDefinition =
EntityIdentifierDefinitionHelper.buildEncapsulatedCompositeIdentifierDefinition( this );
return;
}
entityIdentifierDefinition =
EntityIdentifierDefinitionHelper.buildNonEncapsulatedCompositeIdentifierDefinition( this );
}
private void collectAttributeDefinitions(
Map attributeDefinitionsByName,
EntityMetamodel metamodel) {
for ( int i = 0; i < metamodel.getPropertySpan(); i++ ) {
final AttributeDefinition attributeDefinition = metamodel.getProperties()[i];
// Don't replace an attribute definition if it is already in attributeDefinitionsByName
// because the new value will be from a subclass.
final AttributeDefinition oldAttributeDefinition = attributeDefinitionsByName.get(
attributeDefinition.getName()
);
if ( oldAttributeDefinition != null ) {
if ( LOG.isTraceEnabled() ) {
LOG.tracef(
"Ignoring subclass attribute definition [%s.%s] because it is defined in a superclass ",
entityMetamodel.getName(),
attributeDefinition.getName()
);
}
}
else {
attributeDefinitionsByName.put( attributeDefinition.getName(), attributeDefinition );
}
}
// see if there are any subclass persisters...
final Set subClassEntityNames = metamodel.getSubclassEntityNames();
if ( subClassEntityNames == null ) {
return;
}
// see if we can find the persisters...
for ( String subClassEntityName : subClassEntityNames ) {
if ( metamodel.getName().equals( subClassEntityName ) ) {
// skip it
continue;
}
try {
final EntityPersister subClassEntityPersister = factory.getEntityPersister( subClassEntityName );
collectAttributeDefinitions( attributeDefinitionsByName, subClassEntityPersister.getEntityMetamodel() );
}
catch (MappingException e) {
throw new IllegalStateException(
String.format(
"Could not locate subclass EntityPersister [%s] while processing EntityPersister [%s]",
subClassEntityName,
metamodel.getName()
),
e
);
}
}
}
private void collectAttributeDefinitions() {
// todo : I think this works purely based on luck atm
// specifically in terms of the sub/super class entity persister(s) being available. Bit of chicken-egg
// problem there:
// * If I do this during postConstruct (as it is now), it works as long as the
// super entity persister is already registered, but I don't think that is necessarily true.
// * If I do this during postInstantiate then lots of stuff in postConstruct breaks if we want
// to try and drive SQL generation on these (which we do ultimately). A possible solution there
// would be to delay all SQL generation until postInstantiate
Map attributeDefinitionsByName = new LinkedHashMap();
collectAttributeDefinitions( attributeDefinitionsByName, getEntityMetamodel() );
// EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel();
// while ( currentEntityMetamodel != null ) {
// for ( int i = 0; i < currentEntityMetamodel.getPropertySpan(); i++ ) {
// attributeDefinitions.add( currentEntityMetamodel.getProperties()[i] );
// }
// // see if there is a super class EntityMetamodel
// final String superEntityName = currentEntityMetamodel.getSuperclass();
// if ( superEntityName != null ) {
// currentEntityMetamodel = factory.getEntityPersister( superEntityName ).getEntityMetamodel();
// }
// else {
// currentEntityMetamodel = null;
// }
// }
this.attributeDefinitions = Collections.unmodifiableList(
new ArrayList( attributeDefinitionsByName.values() )
);
// // todo : leverage the attribute definitions housed on EntityMetamodel
// // for that to work, we'd have to be able to walk our super entity persister(s)
// this.attributeDefinitions = new Iterable() {
// @Override
// public Iterator iterator() {
// return new Iterator() {
//// private final int numberOfAttributes = countSubclassProperties();
//// private final int numberOfAttributes = entityMetamodel.getPropertySpan();
//
// EntityMetamodel currentEntityMetamodel = entityMetamodel;
// int numberOfAttributesInCurrentEntityMetamodel = currentEntityMetamodel.getPropertySpan();
//
// private int currentAttributeNumber;
//
// @Override
// public boolean hasNext() {
// return currentEntityMetamodel != null
// && currentAttributeNumber < numberOfAttributesInCurrentEntityMetamodel;
// }
//
// @Override
// public AttributeDefinition next() {
// final int attributeNumber = currentAttributeNumber;
// currentAttributeNumber++;
// final AttributeDefinition next = currentEntityMetamodel.getProperties()[ attributeNumber ];
//
// if ( currentAttributeNumber >= numberOfAttributesInCurrentEntityMetamodel ) {
// // see if there is a super class EntityMetamodel
// final String superEntityName = currentEntityMetamodel.getSuperclass();
// if ( superEntityName != null ) {
// currentEntityMetamodel = factory.getEntityPersister( superEntityName ).getEntityMetamodel();
// if ( currentEntityMetamodel != null ) {
// numberOfAttributesInCurrentEntityMetamodel = currentEntityMetamodel.getPropertySpan();
// currentAttributeNumber = 0;
// }
// }
// }
//
// return next;
// }
//
// @Override
// public void remove() {
// throw new UnsupportedOperationException( "Remove operation not supported here" );
// }
// };
// }
// };
}
}