HHH-3392 : query cache cluster replication with ResultTransformers

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@15174 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2008-09-10 17:02:45 +00:00
parent 04f2f04801
commit a6ca833e2e
9 changed files with 154 additions and 86 deletions

View File

@ -25,6 +25,7 @@
package org.hibernate.cache;
import java.io.Serializable;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
@ -48,11 +49,16 @@ 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;
/**
* 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 QueryKey(String queryString, QueryParameters queryParameters, Set filters, EntityMode entityMode) {
this.sqlQueryString = queryString;
this.types = queryParameters.getPositionalParameterTypes();
@ -70,7 +76,26 @@ public class QueryKey implements Serializable {
this.entityMode = entityMode;
this.filters = filters;
this.customTransformer = queryParameters.getResultTransformer();
this.hashCode = getHashCode();
this.hashCode = generateHashCode();
}
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<values.length; i++ ) {
result = 37 * result + ( values[i]==null ? 0 : types[i].getHashCode( values[i], entityMode ) );
}
result = 37 * result + ( namedParameters==null ? 0 : namedParameters.hashCode() );
result = 37 * result + ( filters==null ? 0 : filters.hashCode() );
result = 37 * result + ( customTransformer==null ? 0 : customTransformer.hashCode() );
result = 37 * result + sqlQueryString.hashCode();
return result;
}
public boolean equals(Object other) {
@ -98,20 +123,6 @@ public class QueryKey implements Serializable {
public int hashCode() {
return hashCode;
}
private int getHashCode() {
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 ) );
}
result = 37 * result + ( namedParameters==null ? 0 : namedParameters.hashCode() );
result = 37 * result + ( filters==null ? 0 : filters.hashCode() );
result = 37 * result + ( customTransformer==null ? 0 : customTransformer.hashCode() );
result = 37 * result + sqlQueryString.hashCode();
return result;
}
public String toString() {
StringBuffer buf = new StringBuffer()

View File

@ -43,22 +43,22 @@ public interface CriteriaSpecification {
/**
* Each row of results is a <tt>Map</tt> from alias to entity instance
*/
public static final ResultTransformer ALIAS_TO_ENTITY_MAP = new AliasToEntityMapResultTransformer();
public static final ResultTransformer ALIAS_TO_ENTITY_MAP = AliasToEntityMapResultTransformer.INSTANCE;
/**
* Each row of results is an instance of the root entity
*/
public static final ResultTransformer ROOT_ENTITY = new RootEntityResultTransformer();
public static final ResultTransformer ROOT_ENTITY = RootEntityResultTransformer.INSTANCE;
/**
* Each row of results is a distinct instance of the root entity
*/
public static final ResultTransformer DISTINCT_ROOT_ENTITY = new DistinctRootEntityResultTransformer();
public static final ResultTransformer DISTINCT_ROOT_ENTITY = DistinctRootEntityResultTransformer.INSTANCE;
/**
* This result transformer is selected implicitly by calling <tt>setProjection()</tt>
*/
public static final ResultTransformer PROJECTION = new PassThroughResultTransformer();
public static final ResultTransformer PROJECTION = PassThroughResultTransformer.INSTANCE;
/**
* Specifies joining to an entity based on an inner join.

View File

@ -55,7 +55,7 @@ import org.hibernate.property.Setter;
public class AliasToBeanResultTransformer implements ResultTransformer {
// IMPL NOTE : due to the delayed population of setters (setters cached
// for performance), we really cannot pro0perly define equality for
// for performance), we really cannot properly define equality for
// this transformer
private final Class resultClass;

View File

@ -43,14 +43,14 @@ public class AliasToEntityMapResultTransformer extends BasicTransformerAdapter i
public static final AliasToEntityMapResultTransformer INSTANCE = new AliasToEntityMapResultTransformer();
/**
* Instantiate AliasToEntityMapResultTransformer.
*
* @deprecated Use the {@link #INSTANCE} reference instead of explicitly creating a new one.
* Disallow instantiation of AliasToEntityMapResultTransformer.
*/
public AliasToEntityMapResultTransformer() {
// todo : make private
private AliasToEntityMapResultTransformer() {
}
/**
* {@inheritDoc}
*/
public Object transformTuple(Object[] tuple, String[] aliases) {
Map result = new HashMap(tuple.length);
for ( int i=0; i<tuple.length; i++ ) {
@ -70,30 +70,4 @@ public class AliasToEntityMapResultTransformer extends BasicTransformerAdapter i
private Object readResolve() {
return INSTANCE;
}
// all AliasToEntityMapResultTransformer are considered equal ~~~~~~~~~~~~~
/**
* All AliasToEntityMapResultTransformer are considered equal
*
* @param other The other instance to check for equality
* @return True if (non-null) other is a instance of
* AliasToEntityMapResultTransformer.
*/
public boolean equals(Object other) {
// todo : we can remove this once the deprecated ctor can be made private...
return other != null && AliasToEntityMapResultTransformer.class.isInstance( other );
}
/**
* All AliasToEntityMapResultTransformer are considered equal
*
* @return We simply return the hashCode of the
* AliasToEntityMapResultTransformer class name string.
*/
public int hashCode() {
// todo : we can remove this once the deprecated ctor can be made private...
return getClass().getName().hashCode();
}
}

View File

@ -42,11 +42,9 @@ public class DistinctRootEntityResultTransformer implements ResultTransformer, S
public static final DistinctRootEntityResultTransformer INSTANCE = new DistinctRootEntityResultTransformer();
/**
* Instantiate a DistinctRootEntityResultTransformer.
*
* @deprecated Use the {@link #INSTANCE} reference instead of explicitly creating a new one.
* Disallow instantiation of DistinctRootEntityResultTransformer.
*/
public DistinctRootEntityResultTransformer() {
private DistinctRootEntityResultTransformer() {
}
/**
@ -79,8 +77,4 @@ public class DistinctRootEntityResultTransformer implements ResultTransformer, S
return INSTANCE;
}
public boolean equals(Object obj) {
// todo : we can remove this once the deprecated ctor can be made private...
return DistinctRootEntityResultTransformer.class.isInstance( obj );
}
}

View File

@ -36,13 +36,14 @@ public class PassThroughResultTransformer extends BasicTransformerAdapter implem
public static final PassThroughResultTransformer INSTANCE = new PassThroughResultTransformer();
/**
* Instamtiate a PassThroughResultTransformer.
*
* @deprecated Use the {@link #INSTANCE} reference instead of explicitly creating a new one.
* Disallow instantiation of PassThroughResultTransformer.
*/
public PassThroughResultTransformer() {
private PassThroughResultTransformer() {
}
/**
* {@inheritDoc}
*/
public Object transformTuple(Object[] tuple, String[] aliases) {
return tuple.length==1 ? tuple[0] : tuple;
}
@ -56,9 +57,4 @@ public class PassThroughResultTransformer extends BasicTransformerAdapter implem
return INSTANCE;
}
public boolean equals(Object obj) {
// todo : we can remove this once the deprecated ctor can be made private...
return PassThroughResultTransformer.class.isInstance( obj );
}
}

View File

@ -24,7 +24,6 @@
*/
package org.hibernate.transform;
import java.util.List;
import java.io.Serializable;
/**
@ -42,11 +41,9 @@ public final class RootEntityResultTransformer extends BasicTransformerAdapter i
public static final RootEntityResultTransformer INSTANCE = new RootEntityResultTransformer();
/**
* Instantiate RootEntityResultTransformer.
*
* @deprecated Use the {@link #INSTANCE} reference instead of explicitly creating a new one.
* Disallow instantiation of RootEntityResultTransformer.
*/
public RootEntityResultTransformer() {
private RootEntityResultTransformer() {
}
/**
@ -64,9 +61,4 @@ public final class RootEntityResultTransformer extends BasicTransformerAdapter i
private Object readResolve() {
return INSTANCE;
}
public boolean equals(Object obj) {
// todo : we can remove this once the deprecated ctor can be made private...
return RootEntityResultTransformer.class.isInstance( obj );
}
}

View File

@ -0,0 +1,96 @@
/*
* 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.util.Collections;
import java.util.HashMap;
import junit.framework.TestCase;
import org.hibernate.engine.QueryParameters;
import org.hibernate.EntityMode;
import org.hibernate.transform.RootEntityResultTransformer;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.transform.DistinctRootEntityResultTransformer;
import org.hibernate.transform.AliasToEntityMapResultTransformer;
import org.hibernate.transform.PassThroughResultTransformer;
import org.hibernate.transform.DistinctResultTransformer;
import org.hibernate.util.SerializationHelper;
import org.hibernate.util.ArrayHelper;
/**
* Tests relating to {@link QueryKey} instances.
*
* @author Steve Ebersole
*/
public class QueryKeyTest extends TestCase {
private static final String QUERY_STRING = "the query string";
public void testSerializedEquality() {
doTest( buildBasicKey( new QueryParameters() ) );
}
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 ) ) );
}
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(QueryParameters queryParameters) {
return new QueryKey( QUERY_STRING, queryParameters, Collections.EMPTY_SET, EntityMode.POJO );
}
private void doTest(QueryKey key) {
HashMap map = new HashMap();
map.put( key, "" );
assert map.size() == 1 : "really messed up";
Object old = map.put( key, "value" );
assert old != null && map.size() == 1 : "apparent QueryKey equals/hashCode issue";
// finally, lets serialize it and see what happens
QueryKey key2 = ( QueryKey ) SerializationHelper.clone( key );
assert key != key2 : "deep copy issue";
old = map.put( key2, "new value" );
assert old != null && map.size() == 1 : "deserialization did not set hashCode or equals properly";
}
}

View File

@ -6,6 +6,7 @@ import java.math.BigInteger;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import junit.framework.Test;
@ -29,6 +30,7 @@ import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
import org.hibernate.transform.DistinctRootEntityResultTransformer;
import org.hibernate.transform.Transformers;
import org.hibernate.transform.AliasToEntityMapResultTransformer;
import org.hibernate.transform.BasicTransformerAdapter;
/**
* Tests of various features of native SQL queries.
@ -177,7 +179,7 @@ public class NativeSQLQueriesTest extends FunctionalTestCase {
" left outer join EMPLOYMENT emp on org.ORGID = emp.EMPLOYER, ORGANIZATION org2" )
.addEntity("org", Organization.class)
.addJoin("emp", "org.employments")
.setResultTransformer(new DistinctRootEntityResultTransformer())
.setResultTransformer( DistinctRootEntityResultTransformer.INSTANCE )
.list();
assertEquals( l.size(), 2 );
@ -608,13 +610,16 @@ public class NativeSQLQueriesTest extends FunctionalTestCase {
}
}
private static class UpperCasedAliasToEntityMapResultTransformer extends AliasToEntityMapResultTransformer {
private static class UpperCasedAliasToEntityMapResultTransformer extends BasicTransformerAdapter implements Serializable {
public Object transformTuple(Object[] tuple, String[] aliases) {
String[] ucAliases = new String[aliases.length];
for ( int i = 0; i < aliases.length; i++ ) {
ucAliases[i] = aliases[i].toUpperCase();
Map result = new HashMap( tuple.length );
for ( int i = 0; i < tuple.length; i++ ) {
String alias = aliases[i];
if ( alias != null ) {
result.put( alias.toUpperCase(), tuple[i] );
}
}
return super.transformTuple( tuple, ucAliases );
return result;
}
}
}