Bug 501272 - JPQL named query using 'select new' syntax does not return cached results from query results cache - Fix to QueryImpl.getDatabaseQueryInternal() to not reset a Query's uninitialized first result index, unless the parameter is greater than 0 (default for ReadQuery) - Test testSelectNewJPQLQueryWithQueryResultsCache added to JUnitJPQLUnitTestSuite - Test a JPQL query with 'select new' syntax with a named query and query results cache enabled Signed-off-by: David Minsky <david.minsky@oracle.com> Reviewed-by: Petros Splinakis <petros.splinakis@oracle.com>
diff --git a/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/advanced/Room.java b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/advanced/Room.java index e66b6db..7ced930 100644 --- a/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/advanced/Room.java +++ b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/advanced/Room.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012, 2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. @@ -21,11 +21,26 @@ import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.QueryHint; import javax.persistence.OneToMany; import javax.persistence.Table; +import org.eclipse.persistence.config.QueryHints; + @Entity @Table(name="CMP3_ROOM") +@NamedQueries({ + //Bug 501272 + @NamedQuery( + name = "room.findSimpleRoomByWidthLength", + query = "SELECT NEW org.eclipse.persistence.testing.models.jpa.advanced.SimpleRoom(r.id, r.width, r.length) " + + "FROM Room r WHERE r.width = :width AND r.length = :length", + hints = { + @QueryHint(name = QueryHints.QUERY_RESULTS_CACHE, value = "true"), + }) +}) public class Room implements Serializable, Cloneable { @Id private int id;
diff --git a/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/advanced/SimpleRoom.java b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/advanced/SimpleRoom.java new file mode 100644 index 0000000..93871e4 --- /dev/null +++ b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/advanced/SimpleRoom.java
@@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 1998, 2016 Oracle and/or its affiliates. All rights reserved. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 + * which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * dminsky - initial API and implementation + ******************************************************************************/ +package org.eclipse.persistence.testing.models.jpa.advanced; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class SimpleRoom { + + @Id + protected int id; + protected int width; + protected int length; + + public SimpleRoom() { + super(); + } + + public SimpleRoom(int id, int width, int length) { + this(); + this.id = id; + this.width = width; + this.length = length; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + public int getLength() { + return length; + } + + public void setLength(int length) { + this.length = length; + } + +}
diff --git a/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/jpql/JUnitJPQLUnitTestSuite.java b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/jpql/JUnitJPQLUnitTestSuite.java index 8027fb3..4867d2d 100644 --- a/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/jpql/JUnitJPQLUnitTestSuite.java +++ b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/jpql/JUnitJPQLUnitTestSuite.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2016 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. @@ -31,11 +31,14 @@ import org.eclipse.persistence.queries.ReportQuery; import org.eclipse.persistence.queries.ReportQueryResult; import org.eclipse.persistence.testing.models.jpa.advanced.EmployeePopulator; +import org.eclipse.persistence.testing.framework.QuerySQLTracker; import org.eclipse.persistence.testing.framework.junit.JUnitTestCase; import org.eclipse.persistence.sessions.DatabaseSession; import org.eclipse.persistence.testing.models.jpa.advanced.AdvancedTableCreator; import org.eclipse.persistence.testing.models.jpa.advanced.Employee; import org.eclipse.persistence.testing.models.jpa.advanced.PhoneNumber; +import org.eclipse.persistence.testing.models.jpa.advanced.Room; +import org.eclipse.persistence.testing.models.jpa.advanced.SimpleRoom; import org.eclipse.persistence.testing.models.jpa.advanced.entities.EntyA; import org.eclipse.persistence.testing.models.jpa.advanced.entities.EntyC; @@ -102,6 +105,7 @@ public static Test suite() if (!isJPA10()) { // JPA 2.0 Tests suite.addTest(new JUnitJPQLUnitTestSuite("testResetFirstResultOnQuery")); + suite.addTest(new JUnitJPQLUnitTestSuite("testSelectNewJPQLQueryWithQueryResultsCache")); } return suite; @@ -611,4 +615,68 @@ public void testCollectionMappingInWhereClause_2() { rollbackTransaction(em); } } + + /** + * Bug 501272 + * Test a JPQL query with 'select new' syntax with a named query and query results + * cache enabled. Tests if any SQL is generated & correct results after initial query. + * @see SimpleRoom + */ + public void testSelectNewJPQLQueryWithQueryResultsCache() { + EntityManager em = createEntityManager(); + QuerySQLTracker tracker = null; + + // create test data and clear cache + beginTransaction(em); + Room testRoom = new Room(); + final int width = 10; + final int length = 8; + testRoom.setWidth(width); + testRoom.setLength(length); + testRoom.setHeight(12); // completeness + em.persist(testRoom); + commitTransaction(em); + clearCache(); + + em = createEntityManager(); + + try { + int numberOfExecutions = 6; + for (int i = 0; i < numberOfExecutions; i++) { + // Uses JPQL 'SELECT NEW' syntax, copying results into the provided Entity constructor + Query query = em.createNamedQuery("room.findSimpleRoomByWidthLength"); + query.setParameter("width", width); + query.setParameter("length", length); + SimpleRoom simpleRoom = (SimpleRoom)query.getSingleResult(); + + assertNotNull("Returned Entity should be non-null", simpleRoom); + assertEquals("width must be the same", simpleRoom.getWidth(), width); + assertEquals("length must be the same", simpleRoom.getLength(), length); + + // create Query SQL tracker after first run, no more SQL should be executed + if (tracker == null) { + tracker = new QuerySQLTracker(getServerSession()); + } + } + + // SQL should not have been executed after the creation of the QuerySQLTracker, + // as the query results cache is enabled for this named query + assertEquals("More SQL queries executed than expected", 0, tracker.getSqlStatements().size()); + } finally { + if (tracker != null) { + tracker.remove(); + } + // remove test data if it exists + if (testRoom != null ) { + Room existingRoom = em.find(Room.class, testRoom.getId()); + if (existingRoom != null) { + beginTransaction(em); + em.remove(em.find(Room.class, testRoom.getId())); + commitTransaction(em); + } + } + closeEntityManager(em); + } + } + }
diff --git a/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/QueryImpl.java b/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/QueryImpl.java index 968e8bb..07bcdd3 100644 --- a/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/QueryImpl.java +++ b/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/QueryImpl.java
@@ -351,7 +351,12 @@ public DatabaseQuery getDatabaseQueryInternal() { } if (this.databaseQuery.isReadQuery()){ this.maxResults = ((ReadQuery)this.databaseQuery).getInternalMax(); - this.firstResultIndex = ((ReadQuery)this.databaseQuery).getFirstResult(); + // Bug 501272 + // Do not reset a Query's uninitialized first result index, unless the parameter is greater than 0 (default for ReadQuery). + int queryFirstResult = ((ReadQuery)this.databaseQuery).getFirstResult(); + if ((this.firstResultIndex != UNDEFINED) || (this.firstResultIndex == UNDEFINED && queryFirstResult > 0)) { + this.firstResultIndex = queryFirstResult; + } } } else { throw new IllegalArgumentException(ExceptionLocalization.buildMessage("unable_to_find_named_query", new Object[] { this.queryName }));