From b130e7e430e4c3f9d2c7e84a12788f3b77b1ad46 Mon Sep 17 00:00:00 2001 From: brmeyer Date: Mon, 8 Oct 2012 21:51:56 -0400 Subject: [PATCH] HHH-4459 Query with Composite Primary Key parameter crashes when query cache is on --- .../component/PojoComponentTuplizer.java | 2 +- .../org/hibernate/type/ComponentType.java | 10 ++- .../test/querycache/CompositeKey.java | 49 +++++++++++++ .../querycache/EntityWithCompositeKey.java | 19 +++++ .../EntityWithStringCompositeKey.java | 23 ++++++ .../test/querycache/QueryCacheTest.java | 72 +++++++++++++++++-- .../test/querycache/StringCompositeKey.java | 51 +++++++++++++ 7 files changed, 219 insertions(+), 7 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/querycache/CompositeKey.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/querycache/EntityWithCompositeKey.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/querycache/EntityWithStringCompositeKey.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/querycache/StringCompositeKey.java diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/PojoComponentTuplizer.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/PojoComponentTuplizer.java index f36ac08939..a9b0d5a9cf 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/PojoComponentTuplizer.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/PojoComponentTuplizer.java @@ -100,7 +100,7 @@ public class PojoComponentTuplizer extends AbstractComponentTuplizer { if ( component == BackrefPropertyAccessor.UNKNOWN ) { return new Object[propertySpan]; } - if ( optimizer != null && optimizer.getAccessOptimizer() != null ) { + else if ( optimizer != null && optimizer.getAccessOptimizer() != null ) { return optimizer.getAccessOptimizer().getPropertyValues( component ); } else { diff --git a/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java b/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java index 74310ca0b7..f1e89b1e4c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java @@ -424,7 +424,15 @@ public class ComponentType extends AbstractType implements CompositeType { public Object[] getPropertyValues(Object component, EntityMode entityMode) throws HibernateException { - return componentTuplizer.getPropertyValues( component ); + if ( component instanceof Object[] ) { + // A few calls to hashCode pass the property values already in an + // Object[] (ex: QueryKey hash codes for cached queries). + // It's easiest to just check for the condition here prior to + // trying reflection. + return ( Object[] ) component; + } else { + return componentTuplizer.getPropertyValues( component ); + } } public void setPropertyValues(Object component, Object[] values, EntityMode entityMode) diff --git a/hibernate-core/src/test/java/org/hibernate/test/querycache/CompositeKey.java b/hibernate-core/src/test/java/org/hibernate/test/querycache/CompositeKey.java new file mode 100644 index 0000000000..565d8a4356 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/querycache/CompositeKey.java @@ -0,0 +1,49 @@ +package org.hibernate.test.querycache; + +import java.io.Serializable; + +import javax.persistence.Embeddable; + +@Embeddable +public class CompositeKey implements Serializable { + + private static final long serialVersionUID = 7950910288405475131L; + + public int a; + + public int b; + + public CompositeKey() { + } + + public CompositeKey(int a, int b) { + this.a = a; + this.b = b; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + a; + result = prime * result + b; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CompositeKey other = (CompositeKey) obj; + if (a != other.a) + return false; + if (b != other.b) + return false; + return true; + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/querycache/EntityWithCompositeKey.java b/hibernate-core/src/test/java/org/hibernate/test/querycache/EntityWithCompositeKey.java new file mode 100644 index 0000000000..7e91bcfb7b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/querycache/EntityWithCompositeKey.java @@ -0,0 +1,19 @@ +package org.hibernate.test.querycache; + +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; + +@Entity +public class EntityWithCompositeKey { + + @EmbeddedId + public CompositeKey pk; + + public EntityWithCompositeKey() { + } + + public EntityWithCompositeKey(CompositeKey pk) { + this.pk = pk; + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/querycache/EntityWithStringCompositeKey.java b/hibernate-core/src/test/java/org/hibernate/test/querycache/EntityWithStringCompositeKey.java new file mode 100644 index 0000000000..6b3dfd7576 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/querycache/EntityWithStringCompositeKey.java @@ -0,0 +1,23 @@ +package org.hibernate.test.querycache; + +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; + +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; + +@Entity +@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) +public class EntityWithStringCompositeKey { + + private StringCompositeKey pk; + + @EmbeddedId + public StringCompositeKey getPk() { + return pk; + } + + public void setPk(StringCompositeKey pk) { + this.pk = pk; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/querycache/QueryCacheTest.java b/hibernate-core/src/test/java/org/hibernate/test/querycache/QueryCacheTest.java index a64051dde7..a33aa1cca0 100755 --- a/hibernate-core/src/test/java/org/hibernate/test/querycache/QueryCacheTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/querycache/QueryCacheTest.java @@ -23,17 +23,21 @@ */ package org.hibernate.test.querycache; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.util.ArrayList; import java.util.List; import java.util.Map; -import org.junit.Test; - +import org.hibernate.Criteria; import org.hibernate.Hibernate; +import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; +import org.hibernate.criterion.Restrictions; import org.hibernate.stat.EntityStatistics; import org.hibernate.stat.QueryStatistics; import org.hibernate.testing.DialectChecks; @@ -41,19 +45,31 @@ import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.transform.Transformers; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import org.junit.Test; /** * @author Gavin King + * @author Brett Meyer */ public class QueryCacheTest extends BaseCoreFunctionalTestCase { + + private static final CompositeKey PK = new CompositeKey(1, 2); + @Override public String[] getMappings() { return new String[] { "querycache/Item.hbm.xml" }; } + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + CompositeKey.class, + EntityWithCompositeKey.class, + StringCompositeKey.class, + EntityWithStringCompositeKey.class + }; + } + @Override public void configure(Configuration cfg) { super.configure( cfg ); @@ -431,6 +447,52 @@ public class QueryCacheTest extends BaseCoreFunctionalTestCase { assertEquals( qs.getExecutionCount(), 3 ); assertEquals( es.getFetchCount(), 0 ); //check that it was being cached } + + @Test + @TestForIssue( jiraKey = "HHH-4459" ) + public void testGetByCompositeId() { + Session s = openSession(); + s.beginTransaction(); + s.persist( new EntityWithCompositeKey( PK ) ); + Query query = s.createQuery( "FROM EntityWithCompositeKey e WHERE e.pk = :pk" ); + query.setCacheable( true ); + query.setParameter( "pk", PK ); + assertEquals(1, query.list().size( )); + s.getTransaction().rollback(); + s.close(); + + s = openSession(); + s.beginTransaction(); + EntityWithStringCompositeKey entity = new EntityWithStringCompositeKey(); + StringCompositeKey key = new StringCompositeKey(); + key.setAnalog( "foo1" ); + key.setDevice( "foo2" ); + key.setDeviceType( "foo3" ); + key.setSubstation( "foo4" ); + entity.setPk( key ); + s.persist( entity ); + Criteria c = s.createCriteria( + EntityWithStringCompositeKey.class ).add( Restrictions.eq( + "pk", key ) ); + c.setCacheable( true ); + assertEquals( 1, c.list().size() ); + s.getTransaction().rollback(); + s.close(); + } + +// @Test +// public void testGetByCompositeIdNoCache() { +// Query query = em.createQuery("FROM EntityWithCompositeKey e WHERE e.pk = :pk"); +// query.setParameter("pk", PK); +// assertEquals(1, query.getResultList().size()); +// } +// +// @Test +// public void testGetByEntityIself() { +// Query query = em.createQuery("FROM EntityWithCompositeKey e WHERE e = :ent"); +// query.setParameter("ent", new EntityWithCompositeKey(PK)); +// assertEquals(1, query.getResultList().size()); +// } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/querycache/StringCompositeKey.java b/hibernate-core/src/test/java/org/hibernate/test/querycache/StringCompositeKey.java new file mode 100644 index 0000000000..55c4a138c3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/querycache/StringCompositeKey.java @@ -0,0 +1,51 @@ +package org.hibernate.test.querycache; + +import java.io.Serializable; + +import javax.persistence.Embeddable; + +@Embeddable +public class StringCompositeKey implements Serializable { + + private static final long serialVersionUID = 1L; + + private String substation; + + private String deviceType; + + private String device; + + public String getSubstation() { + return substation; + } + + public void setSubstation(String substation) { + this.substation = substation; + } + + public String getDeviceType() { + return deviceType; + } + + public void setDeviceType(String deviceType) { + this.deviceType = deviceType; + } + + public String getDevice() { + return device; + } + + public void setDevice(String device) { + this.device = device; + } + + public String getAnalog() { + return analog; + } + + public void setAnalog(String analog) { + this.analog = analog; + } + + private String analog; +} \ No newline at end of file