HHH-8312 - named parameters binding are not correct when used within subquery

This commit is contained in:
Strong Liu 2013-06-15 16:43:06 +08:00
parent 0ee13cfa08
commit 363a3b2b58
25 changed files with 462 additions and 236 deletions

View File

@ -162,7 +162,8 @@ abstract class AbstractTransactSQLDialect extends Dialect {
return lockOptions.getLockMode().greaterThan( LockMode.READ ) ? tableName + " holdlock" : tableName;
}
public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map keyColumnNames) {
@Override
public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map<String, String[]> keyColumnNames) {
// TODO: merge additional lockoptions support in Dialect.applyLocksToSql
Iterator itr = aliasedLockOptions.getAliasLockIterator();
StringBuilder buffer = new StringBuilder( sql );

View File

@ -1372,7 +1372,7 @@ public abstract class Dialect implements ConversionContext {
* @param keyColumnNames a map of key columns indexed by aliased table names.
* @return the modified SQL string.
*/
public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map keyColumnNames) {
public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map<String, String[]> keyColumnNames) {
return sql + new ForUpdateFragment( this, aliasedLockOptions, keyColumnNames ).toFragmentString();
}

View File

@ -95,7 +95,7 @@ public class SybaseASE157Dialect extends SybaseASE15Dialect {
return tableName;
}
@Override
public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map keyColumnNames) {
public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map<String, String[]> keyColumnNames) {
return sql + new ForUpdateFragment( this, aliasedLockOptions, keyColumnNames ).toFragmentString();
}

View File

@ -35,7 +35,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class ColumnNameCache {
public static final float LOAD_FACTOR = .75f;
private final Map<String, Integer> columnNameToIndexCache;
private final ConcurrentHashMap<String, Integer> columnNameToIndexCache;
public ColumnNameCache(int columnCount) {
// should *not* need to grow beyond the size of the total number of columns in the rs
@ -53,4 +53,4 @@ public class ColumnNameCache {
return index;
}
}
}
}

View File

@ -68,6 +68,7 @@ import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.IdentitySet;
import org.hibernate.loader.hql.QueryLoader;
import org.hibernate.param.ParameterSpecification;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.type.Type;
@ -97,7 +98,7 @@ public class QueryTranslatorImpl implements FilterTranslator {
private String sql;
private ParameterTranslations paramTranslations;
private List collectedParameterSpecifications;
private List<ParameterSpecification> collectedParameterSpecifications;
/**
@ -557,12 +558,11 @@ public class QueryTranslatorImpl implements FilterTranslator {
public ParameterTranslations getParameterTranslations() {
if ( paramTranslations == null ) {
paramTranslations = new ParameterTranslationsImpl( getWalker().getParameters() );
// paramTranslations = new ParameterTranslationsImpl( collectedParameterSpecifications );
}
return paramTranslations;
}
public List getCollectedParameterSpecifications() {
public List<ParameterSpecification> getCollectedParameterSpecifications() {
return collectedParameterSpecifications;
}

View File

@ -76,7 +76,7 @@ public class SqlGenerator extends SqlGeneratorBase implements ErrorReporter {
private SessionFactoryImplementor sessionFactory;
private LinkedList<SqlWriter> outputStack = new LinkedList<SqlWriter>();
private final ASTPrinter printer = new ASTPrinter( SqlTokenTypes.class );
private List collectedParameters = new ArrayList();
private List<ParameterSpecification> collectedParameters = new ArrayList<ParameterSpecification>();
// handle trace logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -106,7 +106,7 @@ public class SqlGenerator extends SqlGeneratorBase implements ErrorReporter {
LOG.trace( prefix + ruleName );
}
public List getCollectedParameters() {
public List<ParameterSpecification> getCollectedParameters() {
return collectedParameters;
}

View File

@ -452,16 +452,12 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa
this.alias = alias;
}
/**
* {@inheritDoc}
*/
@Override
public String getSqlFragment() {
return persisterDiscriminatorMetadata.getSqlFragment( alias );
}
/**
* {@inheritDoc}
*/
@Override
public Type getResolutionType() {
return persisterDiscriminatorMetadata.getResolutionType();
}
@ -660,21 +656,24 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa
// ParameterContainer impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private List embeddedParameters;
private List<ParameterSpecification> embeddedParameters;
@Override
public void addEmbeddedParameter(ParameterSpecification specification) {
if ( embeddedParameters == null ) {
embeddedParameters = new ArrayList();
embeddedParameters = new ArrayList<ParameterSpecification>();
}
embeddedParameters.add( specification );
}
@Override
public boolean hasEmbeddedParameters() {
return embeddedParameters != null && ! embeddedParameters.isEmpty();
}
@Override
public ParameterSpecification[] getEmbeddedParameters() {
return ( ParameterSpecification[] ) embeddedParameters.toArray( new ParameterSpecification[ embeddedParameters.size() ] );
return embeddedParameters.toArray( new ParameterSpecification[ embeddedParameters.size() ] );
}
public ParameterSpecification getIndexCollectionSelectorParamSpec() {

View File

@ -147,14 +147,14 @@ public class IndexNode extends FromReferenceNode {
}
String selectorExpression = gen.getSQL();
joinSequence.addCondition( collectionTableAlias + '.' + indexCols[0] + " = " + selectorExpression );
List paramSpecs = gen.getCollectedParameters();
List<ParameterSpecification> paramSpecs = gen.getCollectedParameters();
if ( paramSpecs != null ) {
switch ( paramSpecs.size() ) {
case 0 :
// nothing to do
break;
case 1 :
ParameterSpecification paramSpec = ( ParameterSpecification ) paramSpecs.get( 0 );
ParameterSpecification paramSpec = paramSpecs.get( 0 );
paramSpec.setExpectedType( queryableCollection.getIndexType() );
fromElement.setIndexCollectionSelectorParamSpec( paramSpec );
break;
@ -176,39 +176,40 @@ public class IndexNode extends FromReferenceNode {
* In the (rare?) case where the index selector contains multiple parameters...
*/
private static class AggregatedIndexCollectionSelectorParameterSpecifications implements ParameterSpecification {
private final List paramSpecs;
private final List<ParameterSpecification> paramSpecs;
public AggregatedIndexCollectionSelectorParameterSpecifications(List paramSpecs) {
public AggregatedIndexCollectionSelectorParameterSpecifications(List<ParameterSpecification> paramSpecs) {
this.paramSpecs = paramSpecs;
}
@Override
public int bind(PreparedStatement statement, QueryParameters qp, SessionImplementor session, int position)
throws SQLException {
int bindCount = 0;
Iterator itr = paramSpecs.iterator();
while ( itr.hasNext() ) {
final ParameterSpecification paramSpec = ( ParameterSpecification ) itr.next();
for ( ParameterSpecification paramSpec : paramSpecs ) {
bindCount += paramSpec.bind( statement, qp, session, position + bindCount );
}
return bindCount;
}
@Override
public Type getExpectedType() {
return null;
}
@Override
public void setExpectedType(Type expectedType) {
}
@Override
public String renderDisplayInfo() {
return "index-selector [" + collectDisplayInfo() + "]" ;
}
private String collectDisplayInfo() {
StringBuilder buffer = new StringBuilder();
Iterator itr = paramSpecs.iterator();
while ( itr.hasNext() ) {
buffer.append( ( ( ParameterSpecification ) itr.next() ).renderDisplayInfo() );
for ( ParameterSpecification paramSpec : paramSpecs ) {
buffer.append( ( paramSpec ).renderDisplayInfo() );
}
return buffer.toString();
}

View File

@ -79,6 +79,7 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.FetchingScrollableResultsImpl;
import org.hibernate.internal.ScrollableResultsImpl;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable;
@ -109,7 +110,7 @@ import org.hibernate.type.VersionType;
public abstract class Loader {
protected static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, Loader.class.getName());
protected static final boolean DEBUG_ENABLED = LOG.isDebugEnabled();
private final SessionFactoryImplementor factory;
private ColumnNameCache columnNameCache;
@ -931,34 +932,34 @@ public abstract class Loader {
final ArrayList hydratedObjects = entitySpan == 0 ? null : new ArrayList( entitySpan * 10 );
final List results = new ArrayList();
handleEmptyCollections( queryParameters.getCollectionKeys(), rs, session );
EntityKey[] keys = new EntityKey[entitySpan]; //we can reuse it for each row
LOG.trace( "Processing result set" );
int count;
boolean isDebugEnabled = LOG.isDebugEnabled();
for ( count = 0; count < maxRows && rs.next(); count++ ) {
if ( isDebugEnabled )
LOG.debugf( "Result set row: %s", count );
Object result = getRowFromResultSet(
rs,
session,
queryParameters,
lockModesArray,
optionalObjectKey,
hydratedObjects,
keys,
returnProxies,
forcedResultTransformer
);
results.add( result );
if ( createSubselects ) {
subselectResultKeys.add(keys);
keys = new EntityKey[entitySpan]; //can't reuse in this case
}
}
if ( LOG.isTraceEnabled() )
LOG.tracev( "Done processing result set ({0} rows)", count );
handleEmptyCollections( queryParameters.getCollectionKeys(), rs, session );
EntityKey[] keys = new EntityKey[entitySpan]; //we can reuse it for each row
LOG.trace( "Processing result set" );
int count;
for ( count = 0; count < maxRows && rs.next(); count++ ) {
if ( DEBUG_ENABLED )
LOG.debugf( "Result set row: %s", count );
Object result = getRowFromResultSet(
rs,
session,
queryParameters,
lockModesArray,
optionalObjectKey,
hydratedObjects,
keys,
returnProxies,
forcedResultTransformer
);
results.add( result );
if ( createSubselects ) {
subselectResultKeys.add(keys);
keys = new EntityKey[entitySpan]; //can't reuse in this case
}
}
if ( LOG.isTraceEnabled() )
LOG.tracev( "Done processing result set ({0} rows)", count );
initializeEntitiesAndCollections(
hydratedObjects,
@ -980,8 +981,10 @@ public abstract class Loader {
protected boolean hasSubselectLoadableCollections() {
final Loadable[] loadables = getEntityPersisters();
for (int i=0; i<loadables.length; i++ ) {
if ( loadables[i].hasSubselectLoadableCollections() ) return true;
for ( Loadable loadable : loadables ) {
if ( loadable.hasSubselectLoadableCollections() ) {
return true;
}
}
return false;
}
@ -990,8 +993,8 @@ public abstract class Loader {
Set[] result = new Set[ ( ( EntityKey[] ) keys.get(0) ).length ];
for ( int j=0; j<result.length; j++ ) {
result[j] = new HashSet( keys.size() );
for ( int i=0; i<keys.size(); i++ ) {
result[j].add( ( ( EntityKey[] ) keys.get(i) ) [j] );
for ( Object key : keys ) {
result[j].add( ( (EntityKey[]) key )[j] );
}
}
return result;
@ -1037,13 +1040,11 @@ public abstract class Loader {
private Map buildNamedParameterLocMap(QueryParameters queryParameters) {
if ( queryParameters.getNamedParameters()!=null ) {
final Map namedParameterLocMap = new HashMap();
Iterator piter = queryParameters.getNamedParameters().keySet().iterator();
while ( piter.hasNext() ) {
String name = (String) piter.next();
for(String name : queryParameters.getNamedParameters().keySet()){
namedParameterLocMap.put(
name,
getNamedParameterLocs(name)
);
);
}
return namedParameterLocMap;
}
@ -1991,30 +1992,33 @@ public abstract class Loader {
*/
protected int bindNamedParameters(
final PreparedStatement statement,
final Map namedParams,
final Map<String, TypedValue> namedParams,
final int startIndex,
final SessionImplementor session) throws SQLException, HibernateException {
if ( namedParams != null ) {
// assumes that types are all of span 1
Iterator iter = namedParams.entrySet().iterator();
final boolean debugEnabled = LOG.isDebugEnabled();
int result = 0;
while ( iter.hasNext() ) {
Map.Entry e = ( Map.Entry ) iter.next();
String name = ( String ) e.getKey();
TypedValue typedval = ( TypedValue ) e.getValue();
int[] locs = getNamedParameterLocs( name );
for ( int i = 0; i < locs.length; i++ ) {
if ( debugEnabled ) LOG.debugf( "bindNamedParameters() %s -> %s [%s]", typedval.getValue(), name, locs[i] + startIndex );
typedval.getType().nullSafeSet( statement, typedval.getValue(), locs[i] + startIndex, session );
}
result += locs.length;
}
int result = 0;
if ( CollectionHelper.isEmpty( namedParams ) ) {
return result;
}
else {
return 0;
for ( String name : namedParams.keySet() ) {
TypedValue typedValue = namedParams.get( name );
int columnSpan = typedValue.getType().getColumnSpan( getFactory() );
int[] locs = getNamedParameterLocs( name );
for ( int loc : locs ) {
if ( DEBUG_ENABLED ) {
LOG.debugf(
"bindNamedParameters() %s -> %s [%s]",
typedValue.getValue(),
name,
loc + startIndex
);
}
int start = loc * columnSpan + startIndex;
typedValue.getType().nullSafeSet( statement, typedValue.getValue(), start, session );
}
result += locs.length;
}
return result;
}
public int[] getNamedParameterLocs(String name) {
@ -2079,7 +2083,7 @@ public abstract class Loader {
private ColumnNameCache retreiveColumnNameToIndexCache(ResultSet rs) throws SQLException {
if ( columnNameCache == null ) {
LOG.trace( "Building columnName->columnIndex cache" );
LOG.trace( "Building columnName -> columnIndex cache" );
columnNameCache = new ColumnNameCache( rs.getMetaData().getColumnCount() );
}
@ -2292,7 +2296,7 @@ public abstract class Loader {
final Serializable[] ids,
final Object[] parameterValues,
final Type[] parameterTypes,
final Map namedParameters,
final Map<String, TypedValue> namedParameters,
final Type type) throws HibernateException {
Type[] idTypes = new Type[ids.length];
@ -2550,7 +2554,6 @@ public abstract class Loader {
// whether scrolling of their result set should be allowed.
//
// By default it is allowed.
return;
}
/**

View File

@ -35,6 +35,7 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.type.Type;
@ -47,15 +48,15 @@ public class SubselectCollectionLoader extends BasicCollectionLoader {
private final Serializable[] keys;
private final Type[] types;
private final Object[] values;
private final Map namedParameters;
private final Map namedParameterLocMap;
private final Map<String, TypedValue> namedParameters;
private final Map<String, int[]> namedParameterLocMap;
public SubselectCollectionLoader(
QueryableCollection persister,
String subquery,
Collection entityKeys,
QueryParameters queryParameters,
Map namedParameterLocMap,
Map<String, int[]> namedParameterLocMap,
SessionFactoryImplementor factory,
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
super( persister, 1, subquery, factory, loadQueryInfluencers );
@ -74,6 +75,7 @@ public class SubselectCollectionLoader extends BasicCollectionLoader {
}
@Override
public void initialize(Serializable id, SessionImplementor session)
throws HibernateException {
loadCollectionSubselect(
@ -86,8 +88,9 @@ public class SubselectCollectionLoader extends BasicCollectionLoader {
);
}
@Override
public int[] getNamedParameterLocs(String name) {
return (int[]) namedParameterLocMap.get( name );
return namedParameterLocMap.get( name );
}
}

View File

@ -35,6 +35,7 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.type.Type;
@ -47,15 +48,15 @@ public class SubselectOneToManyLoader extends OneToManyLoader {
private final Serializable[] keys;
private final Type[] types;
private final Object[] values;
private final Map namedParameters;
private final Map namedParameterLocMap;
private final Map<String, TypedValue> namedParameters;
private final Map<String, int[]> namedParameterLocMap;
public SubselectOneToManyLoader(
QueryableCollection persister,
String subquery,
Collection entityKeys,
QueryParameters queryParameters,
Map namedParameterLocMap,
Map<String, int[]> namedParameterLocMap,
SessionFactoryImplementor factory,
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
super( persister, 1, subquery, factory, loadQueryInfluencers );
@ -73,6 +74,7 @@ public class SubselectOneToManyLoader extends OneToManyLoader {
this.namedParameterLocMap = namedParameterLocMap;
}
@Override
public void initialize(Serializable id, SessionImplementor session) throws HibernateException {
loadCollectionSubselect(
session,
@ -83,9 +85,9 @@ public class SubselectOneToManyLoader extends OneToManyLoader {
getKeyType()
);
}
@Override
public int[] getNamedParameterLocs(String name) {
return (int[]) namedParameterLocMap.get( name );
return namedParameterLocMap.get( name );
}
}

View File

@ -463,7 +463,7 @@ public class CustomLoader extends Loader {
);
}
if ( loc instanceof Integer ) {
return new int[] { ( ( Integer ) loc ).intValue() };
return new int[] { (Integer) loc };
}
else {
return ArrayHelper.toIntArray( ( List ) loc );

View File

@ -87,7 +87,7 @@ public class QueryLoader extends BasicLoader {
//private Type[] sqlResultTypes;
private Type[] queryReturnTypes;
private final Map sqlAliasByEntityAlias = new HashMap(8);
private final Map<String, String> sqlAliasByEntityAlias = new HashMap<String, String>(8);
private EntityType[] ownerAssociationTypes;
private int[] owners;
@ -208,15 +208,15 @@ public class QueryLoader extends BasicLoader {
public final void validateScrollability() throws HibernateException {
queryTranslator.validateScrollability();
}
@Override
protected boolean needsFetchingScroll() {
return queryTranslator.containsCollectionFetches();
}
@Override
public Loadable[] getEntityPersisters() {
return entityPersisters;
}
@Override
public String[] getAliases() {
return sqlAliases;
}
@ -224,15 +224,15 @@ public class QueryLoader extends BasicLoader {
public String[] getSqlAliasSuffixes() {
return sqlAliasSuffixes;
}
@Override
public String[] getSuffixes() {
return getSqlAliasSuffixes();
}
@Override
public String[] getCollectionSuffixes() {
return collectionSuffixes;
}
@Override
protected String getQueryIdentifier() {
return queryTranslator.getQueryIdentifier();
}
@ -240,6 +240,7 @@ public class QueryLoader extends BasicLoader {
/**
* The SQL query string to be called.
*/
@Override
public String getSQLString() {
return queryTranslator.getSQLString();
}
@ -248,14 +249,15 @@ public class QueryLoader extends BasicLoader {
* An (optional) persister for a collection to be initialized; only collection loaders
* return a non-null value
*/
@Override
protected CollectionPersister[] getCollectionPersisters() {
return collectionPersisters;
}
@Override
protected int[] getCollectionOwners() {
return collectionOwners;
}
@Override
protected boolean[] getEntityEagerPropertyFetches() {
return entityEagerPropertyFetches;
}
@ -264,16 +266,17 @@ public class QueryLoader extends BasicLoader {
* An array of indexes of the entity that owns a one-to-one association
* to the entity at the given index (-1 if there is no "owner")
*/
@Override
protected int[] getOwners() {
return owners;
}
@Override
protected EntityType[] getOwnerAssociationTypes() {
return ownerAssociationTypes;
}
// -- Loader overrides --
@Override
protected boolean isSubselectLoadingEnabled() {
return hasSubselectLoadableCollections();
}
@ -281,6 +284,7 @@ public class QueryLoader extends BasicLoader {
/**
* @param lockOptions a collection of lock modes specified dynamically via the Query interface
*/
@Override
protected LockMode[] getLockModes(LockOptions lockOptions) {
if ( lockOptions == null ) {
return defaultLockModes;
@ -340,16 +344,14 @@ public class QueryLoader extends BasicLoader {
// we need both the set of locks and the columns to reference in locks
// as the ultimate output of this section...
final LockOptions locks = new LockOptions( lockOptions.getLockMode() );
final Map keyColumnNames = dialect.forUpdateOfColumns() ? new HashMap() : null;
final Map<String, String[]> keyColumnNames = dialect.forUpdateOfColumns() ? new HashMap<String, String[]>() : null;
locks.setScope( lockOptions.getScope() );
locks.setTimeOut( lockOptions.getTimeOut() );
final Iterator itr = sqlAliasByEntityAlias.entrySet().iterator();
while ( itr.hasNext() ) {
final Map.Entry entry = (Map.Entry) itr.next();
final String userAlias = (String) entry.getKey();
final String drivingSqlAlias = (String) entry.getValue();
for ( Map.Entry<String, String> entry : sqlAliasByEntityAlias.entrySet() ) {
final String userAlias = entry.getKey();
final String drivingSqlAlias = entry.getValue();
if ( drivingSqlAlias == null ) {
throw new IllegalArgumentException( "could not locate alias to apply lock mode : " + userAlias );
}
@ -359,8 +361,8 @@ public class QueryLoader extends BasicLoader {
// the exception case here is joined-subclass hierarchies where we instead
// want to apply the lock against the root table (for all other strategies,
// it just happens that driving and root are the same).
final QueryNode select = ( QueryNode ) queryTranslator.getSqlAST();
final Lockable drivingPersister = ( Lockable ) select.getFromClause()
final QueryNode select = (QueryNode) queryTranslator.getSqlAST();
final Lockable drivingPersister = (Lockable) select.getFromClause()
.findFromElementByUserOrSqlAlias( userAlias, drivingSqlAlias )
.getQueryable();
final String sqlAlias = drivingPersister.getRootTableAlias( drivingSqlAlias );
@ -376,7 +378,7 @@ public class QueryLoader extends BasicLoader {
// apply the collected locks and columns
return dialect.applyLocksToSql( sql, locks, keyColumnNames );
}
@Override
protected void applyPostLoadLocks(Object[] row, LockMode[] lockModesArray, SessionImplementor session) {
// todo : scalars???
// if ( row.length != lockModesArray.length ) {
@ -392,7 +394,7 @@ public class QueryLoader extends BasicLoader {
// }
// }
}
@Override
protected boolean upgradeLocks() {
return true;
}
@ -400,18 +402,18 @@ public class QueryLoader extends BasicLoader {
private boolean hasSelectNew() {
return aggregatedSelectExpression != null && aggregatedSelectExpression.getResultTransformer() != null;
}
@Override
protected String[] getResultRowAliases() {
return queryReturnAliases;
}
@Override
protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) {
final ResultTransformer implicitResultTransformer = aggregatedSelectExpression == null
? null
: aggregatedSelectExpression.getResultTransformer();
return HolderInstantiator.resolveResultTransformer( implicitResultTransformer, resultTransformer );
}
@Override
protected boolean[] includeInResultRow() {
boolean[] includeInResultTuple = includeInSelect;
if ( hasScalars ) {
@ -420,7 +422,7 @@ public class QueryLoader extends BasicLoader {
}
return includeInResultTuple;
}
@Override
protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
throws SQLException, HibernateException {
@ -432,6 +434,7 @@ public class QueryLoader extends BasicLoader {
);
}
@Override
protected Object[] getResultRow(Object[] row, ResultSet rs, SessionImplementor session)
throws SQLException, HibernateException {
Object[] resultRow;
@ -449,6 +452,8 @@ public class QueryLoader extends BasicLoader {
return resultRow;
}
@SuppressWarnings("unchecked")
@Override
protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
// meant to handle dynamic instantiation queries...
HolderInstantiator holderInstantiator = buildHolderInstantiator( resultTransformer );
@ -578,6 +583,7 @@ public class QueryLoader extends BasicLoader {
/**
* Returns the locations of all occurrences of the named parameter.
*/
@Override
public int[] getNamedParameterLocs(String name) throws QueryException {
return queryTranslator.getParameterTranslations().getNamedParameterSqlLocations( name );
}
@ -585,7 +591,7 @@ public class QueryLoader extends BasicLoader {
/**
* We specifically override this method here, because in general we know much more
* about the parameters and their appropriate bind positions here then we do in
* our super because we track them explciitly here through the ParameterSpecification
* our super because we track them explicitly here through the ParameterSpecification
* interface.
*
* @param queryParameters The encapsulation of the parameter values to be bound.
@ -594,45 +600,17 @@ public class QueryLoader extends BasicLoader {
* @return The number of JDBC bind positions actually bound during this method execution.
* @throws SQLException Indicates problems performing the binding.
*/
@Override
protected int bindParameterValues(
final PreparedStatement statement,
final QueryParameters queryParameters,
final int startIndex,
final SessionImplementor session) throws SQLException {
// int position = bindFilterParameterValues( statement, queryParameters, startIndex, session );
int position = startIndex;
// List parameterSpecs = queryTranslator.getSqlAST().getWalker().getParameters();
List parameterSpecs = queryTranslator.getCollectedParameterSpecifications();
Iterator itr = parameterSpecs.iterator();
while ( itr.hasNext() ) {
ParameterSpecification spec = ( ParameterSpecification ) itr.next();
List<ParameterSpecification> parameterSpecs = queryTranslator.getCollectedParameterSpecifications();
for ( ParameterSpecification spec : parameterSpecs ) {
position += spec.bind( statement, queryParameters, session, position );
}
return position - startIndex;
}
private int bindFilterParameterValues(
PreparedStatement st,
QueryParameters queryParameters,
int position,
SessionImplementor session) throws SQLException {
// todo : better to handle dynamic filters through implicit DynamicFilterParameterSpecification
// see the discussion there in DynamicFilterParameterSpecification's javadocs as to why
// it is currently not done that way.
int filteredParamCount = queryParameters.getFilteredPositionalParameterTypes() == null
? 0
: queryParameters.getFilteredPositionalParameterTypes().length;
int nonfilteredParamCount = queryParameters.getPositionalParameterTypes() == null
? 0
: queryParameters.getPositionalParameterTypes().length;
int filterParamCount = filteredParamCount - nonfilteredParamCount;
for ( int i = 0; i < filterParamCount; i++ ) {
Type type = queryParameters.getFilteredPositionalParameterTypes()[i];
Object value = queryParameters.getFilteredPositionalParameterValues()[i];
type.nullSafeSet( st, value, position, session );
position += type.getColumnSpan( getFactory() );
}
return position;
}
}

View File

@ -46,30 +46,22 @@ public abstract class AbstractExplicitParameterSpecification implements Explicit
this.sourceColumn = sourceColumn;
}
/**
* {@inheritDoc}
*/
@Override
public int getSourceLine() {
return sourceLine;
}
/**
* {@inheritDoc}
*/
@Override
public int getSourceColumn() {
return sourceColumn;
}
/**
* {@inheritDoc}
*/
@Override
public Type getExpectedType() {
return expectedType;
}
/**
* {@inheritDoc}
*/
@Override
public void setExpectedType(Type expectedType) {
this.expectedType = expectedType;
}

View File

@ -55,9 +55,7 @@ public class CollectionFilterKeyParameterSpecification implements ParameterSpeci
this.queryParameterPosition = queryParameterPosition;
}
/**
* {@inheritDoc}
*/
@Override
public int bind(
PreparedStatement statement,
QueryParameters qp,
@ -68,23 +66,17 @@ public class CollectionFilterKeyParameterSpecification implements ParameterSpeci
return keyType.getColumnSpan( session.getFactory() );
}
/**
* {@inheritDoc}
*/
@Override
public Type getExpectedType() {
return keyType;
}
/**
* {@inheritDoc}
*/
@Override
public void setExpectedType(Type expectedType) {
// todo : throw exception?
}
/**
* {@inheritDoc}
*/
@Override
public String renderDisplayInfo() {
return "collection-filter-key=" + collectionRole;
}

View File

@ -60,9 +60,7 @@ public class DynamicFilterParameterSpecification implements ParameterSpecificati
this.definedParameterType = definedParameterType;
}
/**
* {@inheritDoc}
*/
@Override
public int bind(
PreparedStatement statement,
QueryParameters qp,
@ -85,23 +83,17 @@ public class DynamicFilterParameterSpecification implements ParameterSpecificati
}
}
/**
* {@inheritDoc}
*/
@Override
public Type getExpectedType() {
return definedParameterType;
}
/**
* {@inheritDoc}
*/
@Override
public void setExpectedType(Type expectedType) {
// todo : throw exception? maybe warn if not the same?
}
/**
* {@inheritDoc}
*/
@Override
public String renderDisplayInfo() {
return "dynamic-filter={filterName=" + filterName + ",paramName=" + parameterName + "}";
}

View File

@ -35,7 +35,7 @@ import org.hibernate.engine.spi.TypedValue;
*
* @author Steve Ebersole
*/
public class NamedParameterSpecification extends AbstractExplicitParameterSpecification implements ParameterSpecification {
public class NamedParameterSpecification extends AbstractExplicitParameterSpecification {
private final String name;
/**

View File

@ -35,7 +35,7 @@ import org.hibernate.type.Type;
*
* @author Steve Ebersole
*/
public class PositionalParameterSpecification extends AbstractExplicitParameterSpecification implements ParameterSpecification {
public class PositionalParameterSpecification extends AbstractExplicitParameterSpecification {
private final int hqlPosition;
/**

View File

@ -37,7 +37,7 @@ import org.hibernate.type.VersionType;
* @author Steve Ebersole
*/
public class VersionTypeSeedParameterSpecification implements ParameterSpecification {
private VersionType type;
private final VersionType type;
/**
* Constructs a version seed parameter bind specification.
@ -48,32 +48,24 @@ public class VersionTypeSeedParameterSpecification implements ParameterSpecifica
this.type = type;
}
/**
* {@inheritDoc}
*/
@Override
public int bind(PreparedStatement statement, QueryParameters qp, SessionImplementor session, int position)
throws SQLException {
type.nullSafeSet( statement, type.seed( session ), position, session );
return 1;
}
/**
* {@inheritDoc}
*/
@Override
public Type getExpectedType() {
return type;
}
/**
* {@inheritDoc}
*/
@Override
public void setExpectedType(Type expectedType) {
// expected type is intrinsic here...
}
/**
* {@inheritDoc}
*/
@Override
public String renderDisplayInfo() {
return "version-seed, type=" + type;
}

View File

@ -47,7 +47,7 @@ public class ForUpdateFragment {
this.dialect = dialect;
}
public ForUpdateFragment(Dialect dialect, LockOptions lockOptions, Map keyColumnNames) throws QueryException {
public ForUpdateFragment(Dialect dialect, LockOptions lockOptions, Map<String, String[]> keyColumnNames) throws QueryException {
this( dialect );
LockMode upgradeType = null;
Iterator iter = lockOptions.getAliasLockIterator();
@ -67,13 +67,13 @@ public class ForUpdateFragment {
if ( LockMode.READ.lessThan( lockMode ) ) {
final String tableAlias = ( String ) me.getKey();
if ( dialect.forUpdateOfColumns() ) {
String[] keyColumns = ( String[] ) keyColumnNames.get( tableAlias ); //use the id column alias
String[] keyColumns = keyColumnNames.get( tableAlias ); //use the id column alias
if ( keyColumns == null ) {
throw new IllegalArgumentException( "alias not found: " + tableAlias );
}
keyColumns = StringHelper.qualify( tableAlias, keyColumns );
for ( int i = 0; i < keyColumns.length; i++ ) {
addTableAlias( keyColumns[i] );
for ( String keyColumn : keyColumns ) {
addTableAlias( keyColumn );
}
}
else {

View File

@ -119,8 +119,8 @@ public class ComponentType extends AbstractType implements CompositeType {
int n = 0;
for ( int i = 0; i < propertySpan; i++ ) {
int[] subtypes = propertyTypes[i].sqlTypes( mapping );
for ( int j = 0; j < subtypes.length; j++ ) {
sqlTypes[n++] = subtypes[j];
for ( int subtype : subtypes ) {
sqlTypes[n++] = subtype;
}
}
return sqlTypes;
@ -326,7 +326,7 @@ public class ComponentType extends AbstractType implements CompositeType {
return old != null;
}
if ( old == null ) {
return current != null;
return true;
}
Object[] currentValues = getPropertyValues( current, session );
Object[] oldValues = ( Object[] ) old;
@ -343,12 +343,12 @@ public class ComponentType extends AbstractType implements CompositeType {
return false;
}
@Override
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
return resolve( hydrate( rs, names, session, owner ), session, owner );
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int begin, SessionImplementor session)
throws HibernateException, SQLException {
@ -359,7 +359,7 @@ public class ComponentType extends AbstractType implements CompositeType {
begin += propertyTypes[i].getColumnSpan( session.getFactory() );
}
}
@Override
public void nullSafeSet(
PreparedStatement st,
Object value,
@ -400,13 +400,13 @@ public class ComponentType extends AbstractType implements CompositeType {
return getPropertyValues( value, entityMode );
}
}
@Override
public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
return nullSafeGet( rs, new String[] {name}, session, owner );
}
@Override
public Object getPropertyValue(Object component, int i, SessionImplementor session)
throws HibernateException {
return getPropertyValue( component, i, entityMode );
@ -416,12 +416,12 @@ public class ComponentType extends AbstractType implements CompositeType {
throws HibernateException {
return componentTuplizer.getPropertyValue( component, i );
}
@Override
public Object[] getPropertyValues(Object component, SessionImplementor session)
throws HibernateException {
return getPropertyValues( component, entityMode );
}
@Override
public Object[] getPropertyValues(Object component, EntityMode entityMode)
throws HibernateException {
if ( component instanceof Object[] ) {
@ -434,40 +434,41 @@ public class ComponentType extends AbstractType implements CompositeType {
return componentTuplizer.getPropertyValues( component );
}
}
@Override
public void setPropertyValues(Object component, Object[] values, EntityMode entityMode)
throws HibernateException {
componentTuplizer.setPropertyValues( component, values );
}
@Override
public Type[] getSubtypes() {
return propertyTypes;
}
@Override
public String getName() {
return "component" + ArrayHelper.toString( propertyNames );
}
@Override
public String toLoggableString(Object value, SessionFactoryImplementor factory)
throws HibernateException {
if ( value == null ) {
return "null";
}
Map result = new HashMap();
if ( entityMode == null ) {
throw new ClassCastException( value.getClass().getName() );
}
Map<String,String> result = new HashMap<String, String>();
Object[] values = getPropertyValues( value, entityMode );
for ( int i = 0; i < propertyTypes.length; i++ ) {
result.put( propertyNames[i], propertyTypes[i].toLoggableString( values[i], factory ) );
}
return StringHelper.unqualify( getName() ) + result.toString();
}
@Override
public String[] getPropertyNames() {
return propertyNames;
}
@Override
public Object deepCopy(Object component, SessionFactoryImplementor factory)
throws HibernateException {
if ( component == null ) {
@ -490,7 +491,7 @@ public class ComponentType extends AbstractType implements CompositeType {
return result;
}
@Override
public Object replace(
Object original,
Object target,
@ -576,11 +577,11 @@ public class ComponentType extends AbstractType implements CompositeType {
return result;
}
@Override
public CascadeStyle getCascadeStyle(int i) {
return cascade[i];
}
@Override
public boolean isMutable() {
return true;
}
@ -619,7 +620,7 @@ public class ComponentType extends AbstractType implements CompositeType {
return result;
}
}
@Override
public FetchMode getFetchMode(int i) {
return joinedFetch[i];
}
@ -680,7 +681,7 @@ public class ComponentType extends AbstractType implements CompositeType {
//for components with many-to-one associations
return resolve( value, session, owner );
}
@Override
public boolean[] getPropertyNullability() {
return propertyNullability;
}
@ -689,15 +690,15 @@ public class ComponentType extends AbstractType implements CompositeType {
public boolean isXMLElement() {
return true;
}
@Override
public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException {
return xml;
}
@Override
public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory) throws HibernateException {
replaceNode( node, ( Element ) value );
}
@Override
public boolean[] toColumnNullness(Object value, Mapping mapping) {
boolean[] result = new boolean[ getColumnSpan( mapping ) ];
if ( value == null ) {
@ -712,7 +713,7 @@ public class ComponentType extends AbstractType implements CompositeType {
}
return result;
}
@Override
public boolean isEmbedded() {
return false;
}

View File

@ -0,0 +1,89 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.test.subselect;
import java.util.ArrayList;
import java.util.List;
import junit.framework.Assert;
import org.junit.Test;
import org.hibernate.Session;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
/**
* @author Strong Liu <stliu@hibernate.org>
*/
@SkipForDialect(value = H2Dialect.class, comment = "H2 doesn't support this sql syntax")
@TestForIssue( jiraKey = "HHH-8312")
public class CompositeIdTypeBindingTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Employee.class, EmployeeGroup.class };
}
@Test
public void testCompositeTypeBinding() {
Session session = openSession();
session.beginTransaction();
EmployeeGroup employeegroup = new EmployeeGroup( new EmployeeGroupId( "a", "b" ) );
employeegroup.addEmployee( new Employee( "stliu" ) );
employeegroup.addEmployee( new Employee( "david" ) );
session.save( employeegroup );
employeegroup = new EmployeeGroup( new EmployeeGroupId( "c", "d" ) );
employeegroup.addEmployee( new Employee( "gail" ) );
employeegroup.addEmployee( new Employee( "steve" ) );
session.save( employeegroup );
session.getTransaction().commit();
session.close();
session = openSession();
List<EmployeeGroupId> parameters = new ArrayList<EmployeeGroupId>();
parameters.add( new EmployeeGroupId( "a", "b" ) );
parameters.add( new EmployeeGroupId( "c", "d" ) );
parameters.add( new EmployeeGroupId( "e", "f" ) );
List result = session.createQuery( "select eg from EmployeeGroup eg where eg.id in (:employeegroupIds)" )
.setParameterList( "employeegroupIds", parameters ).list();
Assert.assertEquals( 2, result.size() );
employeegroup = (EmployeeGroup) result.get( 0 );
Assert.assertEquals( "a", employeegroup.getId().getGroupName() );
Assert.assertNotNull( employeegroup.getEmployees() );
Assert.assertEquals( 2, employeegroup.getEmployees().size() );
session.close();
}
}

View File

@ -0,0 +1,53 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.test.subselect;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Employee {
@Id
@GeneratedValue
private Long id;
private String name;
public String getName() {
return name;
}
@SuppressWarnings("unused")
private Employee() {
}
public Employee(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}

View File

@ -0,0 +1,70 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.test.subselect;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
@Entity
public class EmployeeGroup {
@Id
private EmployeeGroupId id;
@OneToMany(cascade = CascadeType.ALL)
@Fetch(FetchMode.SUBSELECT)
private List<Employee> employees = new ArrayList<Employee>();
public EmployeeGroup(EmployeeGroupId id) {
this.id = id;
}
@SuppressWarnings("unused")
private EmployeeGroup() {
}
public boolean addEmployee(Employee employee) {
return employees.add(employee);
}
public List<Employee> getEmployees() {
return employees;
}
public EmployeeGroupId getId() {
return id;
}
@Override
public String toString() {
return id.toString();
}
}

View File

@ -0,0 +1,58 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.test.subselect;
import java.io.Serializable;
import javax.persistence.Embeddable;
@Embeddable
public class EmployeeGroupId
implements Serializable {
private static final long serialVersionUID = 1L;
private String groupName;
private String departmentName;
@SuppressWarnings("unused")
private EmployeeGroupId() {
}
public EmployeeGroupId(String groupName, String departmentName) {
this.groupName = groupName;
this.departmentName = departmentName;
}
public String getDepartmentName() {
return departmentName;
}
public String getGroupName() {
return groupName;
}
@Override
public String toString() {
return "groupName: " + groupName + ", departmentName: " + departmentName;
}
}