org.hibernate.cache.spi.QueryKey Maven / Gradle / Ivy
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or .
*/
package org.hibernate.cache.spi;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.RowSelection;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.transform.CacheableResultTransformer;
import org.hibernate.type.Type;
/**
* 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[] positionalParameterTypes;
private final Object[] positionalParameterValues;
private final Map namedParameters;
private final Integer firstRow;
private final Integer maxRows;
private final String tenantIdentifier;
private final Set filterKeys;
// the explicit user-provided result transformer, not the one used with "select new". Here to avoid mangling
// transformed/non-transformed results.
private final CacheableResultTransformer customTransformer;
/**
* 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;
/**
* 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.
* @param customTransformer The result transformer; should be null if data is not transformed before being cached.
*
* @return The generate query cache key.
*/
public static QueryKey generateQueryKey(
String queryString,
QueryParameters queryParameters,
Set filterKeys,
SharedSessionContractImplementor session,
CacheableResultTransformer customTransformer) {
// 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
final Map namedParameters;
if ( queryParameters.getNamedParameters() == null ) {
namedParameters = null;
}
else {
namedParameters = CollectionHelper.mapOfSize( queryParameters.getNamedParameters().size() );
for ( Map.Entry namedParameterEntry : queryParameters.getNamedParameters().entrySet() ) {
namedParameters.put(
namedParameterEntry.getKey(),
new TypedValue(
namedParameterEntry.getValue().getType(),
namedParameterEntry.getValue().getType().disassemble(
namedParameterEntry.getValue().getValue(),
session,
null
)
)
);
}
}
// decode row selection...
final RowSelection selection = queryParameters.getRowSelection();
final Integer firstRow;
final Integer maxRows;
if ( selection != null ) {
firstRow = selection.getFirstRow();
maxRows = selection.getMaxRows();
}
else {
firstRow = null;
maxRows = null;
}
return new QueryKey(
queryString,
types,
values,
namedParameters,
firstRow,
maxRows,
filterKeys,
session.getTenantIdentifier(),
customTransformer
);
}
/**
* 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 customTransformer Custom result transformer, if one.
* @param tenantIdentifier The tenant identifier in effect for this query, or {@code null}
*/
QueryKey(
String sqlQueryString,
Type[] positionalParameterTypes,
Object[] positionalParameterValues,
Map namedParameters,
Integer firstRow,
Integer maxRows,
Set filterKeys,
String tenantIdentifier,
CacheableResultTransformer customTransformer) {
this.sqlQueryString = sqlQueryString;
this.positionalParameterTypes = positionalParameterTypes;
this.positionalParameterValues = positionalParameterValues;
this.namedParameters = namedParameters;
this.firstRow = firstRow;
this.maxRows = maxRows;
this.tenantIdentifier = tenantIdentifier;
this.filterKeys = filterKeys;
this.customTransformer = customTransformer;
this.hashCode = generateHashCode();
}
/**
* Provides access to the explicitly user-provided result transformer.
*
* @return The result transformer.
*/
public CacheableResultTransformer getResultTransformer() {
return customTransformer;
}
/**
* Provide (unmodifiable) access to the named parameters that are part of this query.
*
* @return The (unmodifiable) map of named parameters
*/
@SuppressWarnings("unchecked")
public Map getNamedParameters() {
return Collections.unmodifiableMap( namedParameters );
}
/**
* 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();
}
private int generateHashCode() {
int result = 13;
result = 37 * result + ( firstRow==null ? 0 : firstRow.hashCode() );
result = 37 * result + ( maxRows==null ? 0 : maxRows.hashCode() );
for ( int i=0; i< positionalParameterValues.length; i++ ) {
result = 37 * result + ( positionalParameterValues[i]==null ? 0 : positionalParameterTypes[i].getHashCode( positionalParameterValues[i] ) );
}
result = 37 * result + ( namedParameters==null ? 0 : namedParameters.hashCode() );
result = 37 * result + ( filterKeys ==null ? 0 : filterKeys.hashCode() );
result = 37 * result + ( customTransformer==null ? 0 : customTransformer.hashCode() );
result = 37 * result + ( tenantIdentifier==null ? 0 : tenantIdentifier.hashCode() );
result = 37 * result + sqlQueryString.hashCode();
return result;
}
@Override
public boolean equals(Object other) {
if ( !( other instanceof QueryKey ) ) {
return false;
}
final QueryKey that = (QueryKey) other;
if ( !sqlQueryString.equals( that.sqlQueryString ) ) {
return false;
}
if ( !Objects.equals( firstRow, that.firstRow ) || !Objects.equals( maxRows, that.maxRows ) ) {
return false;
}
if ( !Objects.equals( customTransformer, that.customTransformer ) ) {
return false;
}
if ( positionalParameterTypes == null ) {
if ( that.positionalParameterTypes != null ) {
return false;
}
}
else {
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] ) ) {
return false;
}
}
}
return Objects.equals( filterKeys, that.filterKeys )
&& Objects.equals( namedParameters, that.namedParameters )
&& Objects.equals( tenantIdentifier, that.tenantIdentifier );
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public String toString() {
final StringBuilder buffer = new StringBuilder( "sql: " ).append( sqlQueryString );
if ( positionalParameterValues != null ) {
buffer.append( "; parameters: " );
for ( Object positionalParameterValue : positionalParameterValues ) {
buffer.append( positionalParameterValue ).append( ", " );
}
}
if ( namedParameters != null ) {
buffer.append( "; named parameters: " ).append( namedParameters );
}
if ( filterKeys != null ) {
buffer.append( "; filterKeys: " ).append( filterKeys );
}
if ( firstRow != null ) {
buffer.append( "; first row: " ).append( firstRow );
}
if ( maxRows != null ) {
buffer.append( "; max rows: " ).append( maxRows );
}
if ( customTransformer != null ) {
buffer.append( "; transformer: " ).append( customTransformer );
}
return buffer.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy