diff --git a/src/org/hibernate/cache/QueryKey.java b/src/org/hibernate/cache/QueryKey.java index 3448a0fa6a..85ed5ca924 100644 --- a/src/org/hibernate/cache/QueryKey.java +++ b/src/org/hibernate/cache/QueryKey.java @@ -1,19 +1,47 @@ -//$Id$ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC. + * + * 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.cache; import java.io.Serializable; +import java.io.IOException; import java.util.Map; import java.util.Set; +import java.util.Iterator; import org.hibernate.EntityMode; import org.hibernate.engine.QueryParameters; import org.hibernate.engine.RowSelection; +import org.hibernate.engine.SessionImplementor; +import org.hibernate.engine.TypedValue; import org.hibernate.transform.ResultTransformer; import org.hibernate.type.Type; import org.hibernate.util.EqualsHelper; +import org.hibernate.util.CollectionHelper; /** * A key that identifies a particular query with bound parameter values + * * @author Gavin King */ public class QueryKey implements Serializable { @@ -25,17 +53,51 @@ public class QueryKey implements Serializable { private final Map namedParameters; private final EntityMode entityMode; private final Set filters; - private final int hashCode; - + // the user provided resulttransformer, not the one used with "select new". Here to avoid mangling transformed/non-transformed results. private final ResultTransformer customTransformer; - - public QueryKey(String queryString, QueryParameters queryParameters, Set filters, EntityMode entityMode) { - this.sqlQueryString = queryString; - this.types = queryParameters.getPositionalParameterTypes(); - this.values = queryParameters.getPositionalParameterValues(); - RowSelection selection = queryParameters.getRowSelection(); - if (selection!=null) { + + /** + * For performance reasons, the hashCode is cached; however, it is marked transient so that it can be + * recalculated as part of the serialization process which allows distributed query caches to work properly. + */ + private transient int hashCode; + + public static QueryKey generateQueryKey( + String queryString, + QueryParameters queryParameters, + Set filters, + SessionImplementor session) { + // disassemble positional parameters + final int positionalParameterCount = queryParameters.getPositionalParameterTypes().length; + final Type[] types = new Type[positionalParameterCount]; + final Object[] values = new Object[positionalParameterCount]; + for ( int i = 0; i < positionalParameterCount; i++ ) { + types[i] = queryParameters.getPositionalParameterTypes()[i]; + values[i] = types[i].disassemble( queryParameters.getPositionalParameterValues()[i], session, null ); + } + + // disassemble named parameters + Map namedParameters = CollectionHelper.mapOfSize( queryParameters.getNamedParameters().size() ); + Iterator itr = queryParameters.getNamedParameters().entrySet().iterator(); + while ( itr.hasNext() ) { + final Map.Entry namedParameterEntry = ( Map.Entry ) itr.next(); + final TypedValue original = ( TypedValue ) namedParameterEntry.getValue(); + namedParameters.put( + namedParameterEntry.getKey(), + new TypedValue( + original.getType(), + original.getType().disassemble( original.getValue(), session, null ), + session.getEntityMode() + ) + ); + } + + // decode row selection... + final RowSelection selection = queryParameters.getRowSelection(); + final Integer firstRow; + final Integer maxRows; + if ( selection != null ) { firstRow = selection.getFirstRow(); maxRows = selection.getMaxRows(); } @@ -43,40 +105,48 @@ public QueryKey(String queryString, QueryParameters queryParameters, Set filters firstRow = null; maxRows = null; } - this.namedParameters = queryParameters.getNamedParameters(); + + return new QueryKey( + queryString, + types, + values, + namedParameters, + firstRow, + maxRows, + filters, + session.getEntityMode(), + queryParameters.getResultTransformer() + ); + } + + /*package*/ QueryKey( + String sqlQueryString, + Type[] types, + Object[] values, + Map namedParameters, + Integer firstRow, + Integer maxRows, + Set filters, + EntityMode entityMode, + ResultTransformer customTransformer) { + this.sqlQueryString = sqlQueryString; + this.types = types; + this.values = values; + this.namedParameters = namedParameters; + this.firstRow = firstRow; + this.maxRows = maxRows; this.entityMode = entityMode; this.filters = filters; - this.customTransformer = queryParameters.getResultTransformer(); - this.hashCode = getHashCode(); + this.customTransformer = customTransformer; + this.hashCode = generateHashCode(); } - - public boolean equals(Object other) { - if ( !( other instanceof QueryKey ) ) return false; - QueryKey that = (QueryKey) other; - if ( !sqlQueryString.equals(that.sqlQueryString) ) return false; - if ( !EqualsHelper.equals(firstRow, that.firstRow) || !EqualsHelper.equals(maxRows, that.maxRows) ) return false; - if ( !EqualsHelper.equals(customTransformer, that.customTransformer) ) return false; - if (types==null) { - if (that.types!=null) return false; - } - else { - if (that.types==null) return false; - if ( types.length!=that.types.length ) return false; - for ( int i=0; i + * Especially helpful for copy map contents. + * + * @param size The size to make the map. + * @return The sized map. + */ + public static Map mapOfSize(int size) { + final int currentSize = (int) (size / 0.75f); + return new HashMap( Math.max( currentSize+ 1, 16), 0.75f ); + } }