HHH-3383 - QueryKey is storing references to entities instead of identifiers
git-svn-id: https://svn.jboss.org/repos/hibernate/core/branches/Branch_3_2@16653 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
db9052b34f
commit
ec853819f8
|
@ -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 class QueryKey implements Serializable {
|
|||
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<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 ( !EqualsHelper.equals(filters, that.filters) ) return false;
|
||||
if ( !EqualsHelper.equals(namedParameters, that.namedParameters) ) return false;
|
||||
return true;
|
||||
|
||||
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
in.defaultReadObject();
|
||||
this.hashCode = generateHashCode();
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
private int getHashCode() {
|
||||
|
||||
private int generateHashCode() {
|
||||
int result = 13;
|
||||
result = 37 * result + ( firstRow==null ? 0 : firstRow.hashCode() );
|
||||
result = 37 * result + ( maxRows==null ? 0 : maxRows.hashCode() );
|
||||
|
@ -90,29 +160,79 @@ public class QueryKey implements Serializable {
|
|||
return result;
|
||||
}
|
||||
|
||||
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(", ");
|
||||
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 (namedParameters!=null) {
|
||||
buf.append("; named parameters: ")
|
||||
.append(namedParameters);
|
||||
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 (filters!=null) {
|
||||
buf.append("; filters: ")
|
||||
.append(filters);
|
||||
|
||||
return EqualsHelper.equals( filters, that.filters )
|
||||
&& EqualsHelper.equals( namedParameters, that.namedParameters );
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
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( ", " );
|
||||
}
|
||||
}
|
||||
if ( namedParameters != null ) {
|
||||
buf.append( "; named parameters: " )
|
||||
.append( namedParameters );
|
||||
}
|
||||
if ( filters != null ) {
|
||||
buf.append( "; filters: " )
|
||||
.append( filters );
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -2117,15 +2117,15 @@ public abstract class Loader {
|
|||
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,
|
||||
|
@ -2134,7 +2134,7 @@ public abstract class Loader {
|
|||
resultTypes,
|
||||
queryCache,
|
||||
key
|
||||
);
|
||||
);
|
||||
|
||||
if ( result == null ) {
|
||||
result = doList( session, queryParameters );
|
||||
|
@ -2146,7 +2146,7 @@ public abstract class Loader {
|
|||
queryCache,
|
||||
key,
|
||||
result
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
return getResultList( result, queryParameters.getResultTransformer() );
|
||||
|
|
|
@ -1,4 +1,26 @@
|
|||
//$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.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -9,14 +31,30 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Various help for handling collections.
|
||||
*
|
||||
* @author Gavin King
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public final class CollectionHelper {
|
||||
|
||||
public static final List EMPTY_LIST = Collections.unmodifiableList( new ArrayList(0) );
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue