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:
parent
04f2f04801
commit
a6ca833e2e
|
@ -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()
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue