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 }));