Author: steve.ebersole(a)jboss.com
Date: 2009-06-01 17:11:43 -0400 (Mon, 01 Jun 2009)
New Revision: 16654
Modified:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/cache/QueryKey.java
core/branches/Branch_3_3/core/src/main/java/org/hibernate/loader/Loader.java
core/branches/Branch_3_3/core/src/main/java/org/hibernate/util/CollectionHelper.java
core/branches/Branch_3_3/core/src/test/java/org/hibernate/cache/QueryKeyTest.java
Log:
HHH-3383 - QueryKey is storing references to entities instead of identifiers
Modified: core/branches/Branch_3_3/core/src/main/java/org/hibernate/cache/QueryKey.java
===================================================================
---
core/branches/Branch_3_3/core/src/main/java/org/hibernate/cache/QueryKey.java 2009-06-01
20:36:35 UTC (rev 16653)
+++
core/branches/Branch_3_3/core/src/main/java/org/hibernate/cache/QueryKey.java 2009-06-01
21:11:43 UTC (rev 16654)
@@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
- *
*/
package org.hibernate.cache;
@@ -28,29 +27,37 @@
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
+ * A key that identifies a particular query with bound parameter values. This is the
object Hibernate uses
+ * as its key into its query cache.
+ *
* @author Gavin King
+ * @author Steve Ebersole
*/
public class QueryKey implements Serializable {
private final String sqlQueryString;
- private final Type[] types;
- private final Object[] values;
+ private final Type[] positionalParameterTypes;
+ private final Object[] positionalParameterValues;
+ private final Map namedParameters;
private final Integer firstRow;
private final Integer maxRows;
- private final Map namedParameters;
private final EntityMode entityMode;
- private final Set filters;
-
- // the user provided resulttransformer, not the one used with "select new".
Here to avoid mangling transformed/non-transformed results.
+ private final Set filterKeys;
+
+ // the user provided resulttransformer, not the one used with "select new".
Here to avoid mangling
+ // transformed/non-transformed results.
private final ResultTransformer customTransformer;
/**
@@ -58,13 +65,52 @@
* recalculated as part of the serialization process which allows distributed query
caches to work properly.
*/
private transient int hashCode;
-
- 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) {
+
+ /**
+ * Generates a QueryKey.
+ *
+ * @param queryString The sql query string.
+ * @param queryParameters The query parameters
+ * @param filterKeys The keys of any enabled filters.
+ * @param session The current session.
+ *
+ * @return The generate query cache key.
+ */
+ public static QueryKey generateQueryKey(
+ String queryString,
+ QueryParameters queryParameters,
+ Set filterKeys,
+ 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();
}
@@ -72,13 +118,63 @@
firstRow = null;
maxRows = null;
}
- this.namedParameters = queryParameters.getNamedParameters();
+
+ return new QueryKey(
+ queryString,
+ types,
+ values,
+ namedParameters,
+ firstRow,
+ maxRows,
+ filterKeys,
+ session.getEntityMode(),
+ queryParameters.getResultTransformer()
+ );
+ }
+
+ /**
+ * Package-protected constructor.
+ *
+ * @param sqlQueryString The sql query string.
+ * @param positionalParameterTypes Positional parameter types.
+ * @param positionalParameterValues Positional parameter values.
+ * @param namedParameters Named parameters.
+ * @param firstRow First row selection, if any.
+ * @param maxRows Max-rows selection, if any.
+ * @param filterKeys Enabled filter keys, if any.
+ * @param entityMode The entity mode.
+ * @param customTransformer Custom result transformer, if one.
+ */
+ QueryKey(
+ String sqlQueryString,
+ Type[] positionalParameterTypes,
+ Object[] positionalParameterValues,
+ Map namedParameters,
+ Integer firstRow,
+ Integer maxRows,
+ Set filterKeys,
+ EntityMode entityMode,
+ ResultTransformer customTransformer) {
+ this.sqlQueryString = sqlQueryString;
+ this.positionalParameterTypes = positionalParameterTypes;
+ this.positionalParameterValues = positionalParameterValues;
+ this.namedParameters = namedParameters;
+ this.firstRow = firstRow;
+ this.maxRows = maxRows;
this.entityMode = entityMode;
- this.filters = filters;
- this.customTransformer = queryParameters.getResultTransformer();
+ this.filterKeys = filterKeys;
+ this.customTransformer = customTransformer;
this.hashCode = generateHashCode();
}
+ /**
+ * Deserialization hook used to re-init the cached hashcode which is needed for proper
clustering support.
+ *
+ * @param in The object input stream.
+ *
+ * @throws IOException Thrown by normal deserialization
+ * @throws ClassNotFoundException Thrown by normal deserialization
+ */
private void readObject(java.io.ObjectInputStream in) throws IOException,
ClassNotFoundException {
in.defaultReadObject();
this.hashCode = generateHashCode();
@@ -88,65 +184,95 @@
int result = 13;
result = 37 * result + ( firstRow==null ? 0 : firstRow.hashCode() );
result = 37 * result + ( maxRows==null ? 0 : maxRows.hashCode() );
- for ( int i=0; i<values.length; i++ ) {
- result = 37 * result + ( values[i]==null ? 0 : types[i].getHashCode( values[i],
entityMode ) );
+ for ( int i=0; i< positionalParameterValues.length; i++ ) {
+ result = 37 * result + ( positionalParameterValues[i]==null ? 0 :
positionalParameterTypes[i].getHashCode( positionalParameterValues[i], entityMode ) );
}
result = 37 * result + ( namedParameters==null ? 0 : namedParameters.hashCode() );
- result = 37 * result + ( filters==null ? 0 : filters.hashCode() );
+ result = 37 * result + ( filterKeys ==null ? 0 : filterKeys.hashCode() );
result = 37 * result + ( customTransformer==null ? 0 : customTransformer.hashCode() );
result = 37 * result + sqlQueryString.hashCode();
return result;
}
- public int hashCode() {
- return hashCode;
- }
-
+ /**
+ * {@inheritDoc}
+ */
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;
+ 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 ( positionalParameterTypes == null ) {
+ if ( that.positionalParameterTypes != null ) {
+ return false;
+ }
+ }
else {
- if (that.types==null) return false;
- if ( types.length!=that.types.length ) return false;
- for ( int i=0; i<types.length; i++ ) {
- if ( types[i].getReturnedClass() != that.types[i].getReturnedClass() ) return false;
- if ( !types[i].isEqual( values[i], that.values[i], entityMode ) ) return false;
+ if ( that.positionalParameterTypes == null ) {
+ return false;
}
+ if ( positionalParameterTypes.length != that.positionalParameterTypes.length ) {
+ return false;
+ }
+ for ( int i = 0; i < positionalParameterTypes.length; i++ ) {
+ if ( positionalParameterTypes[i].getReturnedClass() !=
that.positionalParameterTypes[i].getReturnedClass() ) {
+ return false;
+ }
+ if ( !positionalParameterTypes[i].isEqual( positionalParameterValues[i],
that.positionalParameterValues[i], entityMode ) ) {
+ return false;
+ }
+ }
}
- if ( !EqualsHelper.equals(filters, that.filters) ) return false;
- if ( !EqualsHelper.equals(namedParameters, that.namedParameters) ) return false;
- return true;
+
+ return EqualsHelper.equals( filterKeys, that.filterKeys )
+ && EqualsHelper.equals( namedParameters, that.namedParameters );
}
+ /**
+ * {@inheritDoc}
+ */
+ public int hashCode() {
+ return hashCode;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public String toString() {
StringBuffer buf = new StringBuffer()
- .append("sql: ")
- .append(sqlQueryString);
- if (values!=null) {
- buf.append("; parameters: ");
- for (int i=0; i<values.length; i++) {
- buf.append( values[i] )
- .append(", ");
+ .append( "sql: " )
+ .append( sqlQueryString );
+ if ( positionalParameterValues != null ) {
+ buf.append( "; parameters: " );
+ for ( int i = 0; i < positionalParameterValues.length; i++ ) {
+ buf.append( positionalParameterValues[i] ).append( ", " );
}
}
- if (namedParameters!=null) {
- buf.append("; named parameters: ")
- .append(namedParameters);
+ if ( namedParameters != null ) {
+ buf.append( "; named parameters: " ).append( namedParameters );
}
- if (filters!=null) {
- buf.append("; filters: ")
- .append(filters);
+ if ( filterKeys != null ) {
+ buf.append( "; filterKeys: " ).append( filterKeys );
}
- if (firstRow!=null) buf.append("; first row: ").append(firstRow);
- if (maxRows!=null) buf.append("; max rows: ").append(maxRows);
- if (customTransformer!=null) buf.append("; transformer:
").append(customTransformer);
+ if ( firstRow != null ) {
+ buf.append( "; first row: " ).append( firstRow );
+ }
+ if ( maxRows != null ) {
+ buf.append( "; max rows: " ).append( maxRows );
+ }
+ if ( customTransformer != null ) {
+ buf.append( "; transformer: " ).append( customTransformer );
+ }
return buf.toString();
}
-
+
}
Modified: core/branches/Branch_3_3/core/src/main/java/org/hibernate/loader/Loader.java
===================================================================
---
core/branches/Branch_3_3/core/src/main/java/org/hibernate/loader/Loader.java 2009-06-01
20:36:35 UTC (rev 16653)
+++
core/branches/Branch_3_3/core/src/main/java/org/hibernate/loader/Loader.java 2009-06-01
21:11:43 UTC (rev 16654)
@@ -2138,15 +2138,15 @@
QueryCache queryCache = factory.getQueryCache( queryParameters.getCacheRegion() );
Set filterKeys = FilterKey.createFilterKeys(
- session.getEnabledFilters(),
+ session.getEnabledFilters(),
session.getEntityMode()
- );
- QueryKey key = new QueryKey(
+ );
+ QueryKey key = QueryKey.generateQueryKey(
getSQLString(),
queryParameters,
filterKeys,
- session.getEntityMode()
- );
+ session
+ );
List result = getResultFromQueryCache(
session,
@@ -2155,7 +2155,7 @@
resultTypes,
queryCache,
key
- );
+ );
if ( result == null ) {
result = doList( session, queryParameters );
@@ -2167,7 +2167,7 @@
queryCache,
key,
result
- );
+ );
}
return getResultList( result, queryParameters.getResultTransformer() );
Modified:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/util/CollectionHelper.java
===================================================================
---
core/branches/Branch_3_3/core/src/main/java/org/hibernate/util/CollectionHelper.java 2009-06-01
20:36:35 UTC (rev 16653)
+++
core/branches/Branch_3_3/core/src/main/java/org/hibernate/util/CollectionHelper.java 2009-06-01
21:11:43 UTC (rev 16654)
@@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
- *
*/
package org.hibernate.util;
@@ -32,7 +31,10 @@
import java.util.Map;
/**
+ * Various help for handling collections.
+ *
* @author Gavin King
+ * @author Steve Ebersole
*/
public final class CollectionHelper {
@@ -40,6 +42,19 @@
public static final Collection EMPTY_COLLECTION = Collections.unmodifiableCollection(
new ArrayList(0) );
public static final Map EMPTY_MAP = Collections.unmodifiableMap( new HashMap(0) );
- private CollectionHelper() {}
+ private CollectionHelper() {
+ }
+ /**
+ * Build a properly sized map, especially handling load size and load factor to prevent
immediate resizing.
+ * <p/>
+ * 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 );
+ }
}
Modified:
core/branches/Branch_3_3/core/src/test/java/org/hibernate/cache/QueryKeyTest.java
===================================================================
---
core/branches/Branch_3_3/core/src/test/java/org/hibernate/cache/QueryKeyTest.java 2009-06-01
20:36:35 UTC (rev 16653)
+++
core/branches/Branch_3_3/core/src/test/java/org/hibernate/cache/QueryKeyTest.java 2009-06-01
21:11:43 UTC (rev 16654)
@@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
- *
*/
package org.hibernate.cache;
@@ -29,7 +28,6 @@
import junit.framework.TestCase;
-import org.hibernate.engine.QueryParameters;
import org.hibernate.EntityMode;
import org.hibernate.transform.RootEntityResultTransformer;
import org.hibernate.transform.ResultTransformer;
@@ -49,35 +47,31 @@
private static final String QUERY_STRING = "the query string";
public void testSerializedEquality() {
- doTest( buildBasicKey( new QueryParameters() ) );
+ doTest( buildBasicKey( null ) );
}
public void testSerializedEqualityWithResultTransformer() {
- doTest( buildBasicKey( buildQueryParameters( RootEntityResultTransformer.INSTANCE ) )
);
- doTest( buildBasicKey( buildQueryParameters(
DistinctRootEntityResultTransformer.INSTANCE ) ) );
- doTest( buildBasicKey( buildQueryParameters( DistinctResultTransformer.INSTANCE ) ) );
- doTest( buildBasicKey( buildQueryParameters( AliasToEntityMapResultTransformer.INSTANCE
) ) );
- doTest( buildBasicKey( buildQueryParameters( PassThroughResultTransformer.INSTANCE ) )
);
+ doTest( buildBasicKey( RootEntityResultTransformer.INSTANCE ) );
+ doTest( buildBasicKey( DistinctRootEntityResultTransformer.INSTANCE ) );
+ doTest( buildBasicKey( DistinctResultTransformer.INSTANCE ) );
+ doTest( buildBasicKey( AliasToEntityMapResultTransformer.INSTANCE ) );
+ doTest( buildBasicKey( PassThroughResultTransformer.INSTANCE ) );
}
- private QueryParameters buildQueryParameters(ResultTransformer resultTransformer) {
- return new QueryParameters(
- ArrayHelper.EMPTY_TYPE_ARRAY, // param types
- ArrayHelper.EMPTY_OBJECT_ARRAY, // param values
- Collections.EMPTY_MAP, // lock modes
- null, // row selection
- false, // cacheable?
- "", // cache region
- "", // SQL comment
- false, // is natural key lookup?
- resultTransformer // the result transformer, duh! ;)
+ private QueryKey buildBasicKey(ResultTransformer resultTransformer) {
+ return new QueryKey(
+ QUERY_STRING,
+ ArrayHelper.EMPTY_TYPE_ARRAY, // positional param types
+ ArrayHelper.EMPTY_OBJECT_ARRAY, // positional param values
+ Collections.EMPTY_MAP, // named params
+ null, // firstRow selection
+ null, // maxRows selection
+ Collections.EMPTY_SET, // filter keys
+ EntityMode.POJO, // entity mode
+ resultTransformer // the result transformer
);
}
- private QueryKey buildBasicKey(QueryParameters queryParameters) {
- return new QueryKey( QUERY_STRING, queryParameters, Collections.EMPTY_SET,
EntityMode.POJO );
- }
-
private void doTest(QueryKey key) {
HashMap map = new HashMap();