Fix for bug 430042 (2.5 branch checkin)
- Allow an cache only pk query to succeed when a query is performed on related objects
- Resolves an expression issue when the query string is constructed in a certain order
- Reviewed: cdelahunt
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/expressions/LogicalExpression.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/expressions/LogicalExpression.java
index e9edf1a..c717398 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/expressions/LogicalExpression.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/expressions/LogicalExpression.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 1998, 2013 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2014 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.
@@ -73,6 +73,14 @@
return false;
}
}
+
+ // Ensure that both sides of the expression tree have a session, for correct descriptor, value resolution
+ if (this.secondChild.getSession() == null && this.firstChild.getSession() != null) {
+ this.secondChild.getBuilder().setSession(this.firstChild.getSession());
+ } else if (this.firstChild.getSession() == null && this.secondChild.getSession() != null) {
+ this.firstChild.getBuilder().setSession(this.secondChild.getSession());
+ }
+
boolean validExpression = this.firstChild.extractValues(primaryKeyOnly, requireExactMatch, descriptor, primaryKeyRow, translationRow);
if (requireExactMatch && (!validExpression)) {
return false;
@@ -94,6 +102,14 @@
return false;
}
}
+
+ // Ensure that both sides of the expression tree have a session, for correct descriptor, field resolution
+ if (this.secondChild.getSession() == null && this.firstChild.getSession() != null) {
+ this.secondChild.getBuilder().setSession(this.firstChild.getSession());
+ } else if (this.firstChild.getSession() == null && this.secondChild.getSession() != null) {
+ this.firstChild.getBuilder().setSession(this.secondChild.getSession());
+ }
+
boolean validExpression = this.firstChild.extractFields(requireExactMatch, primaryKey, descriptor, searchFields, foundFields);
if (requireExactMatch && (!validExpression)) {
return false;
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/expressions/RelationExpression.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/expressions/RelationExpression.java
index 28259f4..365005e 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/expressions/RelationExpression.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/expressions/RelationExpression.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 1998, 2013 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2014 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.
@@ -233,6 +233,9 @@
return false;
}
+ // Descriptor to use for child query key
+ ClassDescriptor descriptorForChild = null;
+
// Ensure that the primary key is being queried on.
if (this.firstChild.isFieldExpression()) {
FieldExpression child = (FieldExpression)this.firstChild;
@@ -247,7 +250,12 @@
if (!child.getBaseExpression().isExpressionBuilder()) {
return false;
}
- DatabaseMapping mapping = descriptor.getObjectBuilder().getMappingForAttributeName(child.getName());
+ descriptorForChild = ((ExpressionBuilder)child.getBaseExpression()).getDescriptor();
+ if (descriptorForChild == null) {
+ descriptorForChild = descriptor;
+ }
+ DatabaseMapping mapping = descriptorForChild.getObjectBuilder().getMappingForAttributeName(child.getName());
+
if (mapping != null) {
if (primaryKeyOnly && !mapping.isPrimaryKeyMapping()) {
return false;
@@ -264,7 +272,7 @@
field = ((AbstractColumnMapping)mapping).getField();
} else {
// Only get field for the source object.
- field = descriptor.getObjectBuilder().getFieldForQueryKeyName(child.getName());
+ field = descriptorForChild.getObjectBuilder().getFieldForQueryKeyName(child.getName());
}
} else if (this.secondChild.isFieldExpression()) {
FieldExpression child = (FieldExpression)this.secondChild;
@@ -279,7 +287,12 @@
if (!child.getBaseExpression().isExpressionBuilder()) {
return false;
}
- DatabaseMapping mapping = descriptor.getObjectBuilder().getMappingForAttributeName(child.getName());
+ descriptorForChild = ((ExpressionBuilder)child.getBaseExpression()).getDescriptor();
+ if (descriptorForChild == null) {
+ descriptorForChild = descriptor;
+ }
+ DatabaseMapping mapping = descriptorForChild.getObjectBuilder().getMappingForAttributeName(child.getName());
+
// Only support referencing limited number of relationship types.
if (mapping != null) {
if (primaryKeyOnly && !mapping.isPrimaryKeyMapping()) {
@@ -294,12 +307,26 @@
}
field = ((AbstractColumnMapping)mapping).getField();
} else {
- field = descriptor.getObjectBuilder().getFieldForQueryKeyName(child.getName());
+ field = descriptorForChild.getObjectBuilder().getFieldForQueryKeyName(child.getName());
}
} else {
return false;
}
- if ((field == null) || (primaryKeyOnly && !descriptor.getPrimaryKeyFields().contains(field))) {
+ if (field == null) {
+ return false;
+ }
+ // Check child descriptor's primary key fields if the passed descriptor does not contain the field
+ if (primaryKeyOnly && !descriptor.getPrimaryKeyFields().contains(field)) {
+ if (descriptorForChild != null && descriptorForChild != descriptor && descriptorForChild.getPrimaryKeyFields().contains(field)) {
+ // Child descriptor's pk fields contains the field, return true.
+ // Do not add the field from the query key's descriptor to the primaryKeyRow
+ return true;
+ } else {
+ return false;
+ }
+ }
+ // Do not replace the field in the row with the same field
+ if (primaryKeyRow.get(field) != null) {
return false;
}
primaryKeyRow.put(field, value);
diff --git a/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/jpql/JUnitJPQLParameterTestSuite.java b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/jpql/JUnitJPQLParameterTestSuite.java
index 94291eb..d5d8e53 100644
--- a/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/jpql/JUnitJPQLParameterTestSuite.java
+++ b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/jpql/JUnitJPQLParameterTestSuite.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 1998, 2013 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2014 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.
@@ -13,24 +13,31 @@
package org.eclipse.persistence.testing.tests.jpa.jpql;
-
+import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.Vector;
-import junit.framework.Assert;
import junit.framework.Test;
import junit.framework.TestSuite;
import javax.persistence.Query;
import javax.persistence.EntityManager;
+import org.eclipse.persistence.testing.models.jpa.advanced.Address;
+import org.eclipse.persistence.testing.models.jpa.advanced.Department;
import org.eclipse.persistence.testing.models.jpa.advanced.Employee;
+import org.eclipse.persistence.testing.models.jpa.advanced.Jigsaw;
+import org.eclipse.persistence.testing.models.jpa.advanced.JigsawPiece;
+import org.eclipse.persistence.testing.models.jpa.advanced.PhoneNumber;
import org.eclipse.persistence.testing.models.jpa.advanced.EmployeePopulator;
import org.eclipse.persistence.testing.framework.junit.JUnitTestCase;
import org.eclipse.persistence.sessions.DatabaseSession;
+import org.eclipse.persistence.config.CacheUsage;
+import org.eclipse.persistence.config.QueryHints;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.testing.models.jpa.advanced.AdvancedTableCreator;
+import org.eclipse.persistence.testing.models.relationshipmaintenance.Dept;
/**
* <p>
* <b>Purpose</b>: Test EJBQL parameter functionality.
@@ -80,6 +87,10 @@
suite.addTest(new JUnitJPQLParameterTestSuite("testSetup"));
suite.addTest(new JUnitJPQLParameterTestSuite("multipleParameterTest"));
suite.addTest(new JUnitJPQLParameterTestSuite("updateEnumParameter"));
+ suite.addTest(new JUnitJPQLParameterTestSuite("testQueryParametersCheckCacheTest"));
+ suite.addTest(new JUnitJPQLParameterTestSuite("testQueryParametersDontCheckCacheTest"));
+ suite.addTest(new JUnitJPQLParameterTestSuite("testQueryParametersReversedCheckCacheTest"));
+ suite.addTest(new JUnitJPQLParameterTestSuite("testQueryParametersReversedDontCheckCacheTest"));
if (! JUnitTestCase.isJPA10()) {
suite.addTest(new JUnitJPQLParameterTestSuite("emptyParametersForNonParameterizedNamedQueryTest"));
}
@@ -126,7 +137,7 @@
query.setParameter(3, employee.getId());
List result = query.getResultList();
- Assert.assertTrue("Multiple Parameter Test Case Failed", comparer.compareObjects(result, expectedResult));
+ assertTrue("Multiple Parameter Test Case Failed", comparer.compareObjects(result, expectedResult));
}
// Test for GF#1123 - UPDATE with JPQL does not handle enums correctly.
@@ -184,4 +195,67 @@
assertEquals("Parameters size should be 0", 0, parameters.size());
}
+ public void testQueryParametersCheckCacheTest() {
+ testQueryParametersWithQueryAndCheckCache("SELECT p FROM JigsawPiece p, Jigsaw j WHERE j.id=:jigsawId AND p.id=:jigsawPieceId", true);
+ }
+
+ public void testQueryParametersDontCheckCacheTest() {
+ testQueryParametersWithQueryAndCheckCache("SELECT p FROM JigsawPiece p, Jigsaw j WHERE j.id=:jigsawId AND p.id=:jigsawPieceId", false);
+ }
+
+ public void testQueryParametersReversedCheckCacheTest() {
+ testQueryParametersWithQueryAndCheckCache("SELECT p FROM JigsawPiece p, Jigsaw j WHERE p.id=:jigsawPieceId AND j.id=:jigsawId", true);
+ }
+
+ public void testQueryParametersReversedDontCheckCacheTest() {
+ testQueryParametersWithQueryAndCheckCache("SELECT p FROM JigsawPiece p, Jigsaw j WHERE p.id=:jigsawPieceId AND j.id=:jigsawId", false);
+ }
+
+ // EL bug 430042 - pk query parameter binding incorrect
+ private void testQueryParametersWithQueryAndCheckCache(String jpqlQueryString, boolean checkCacheOnly) {
+ EntityManager em = createEntityManager();
+ Jigsaw jigsawTestData = null;
+ JigsawPiece jigsawPieceTestData = null;
+
+ try {
+ beginTransaction(em);
+
+ jigsawTestData = new Jigsaw();
+ jigsawPieceTestData = new JigsawPiece();
+ jigsawTestData.addPiece(jigsawPieceTestData);
+ em.persist(jigsawTestData);
+
+ commitTransaction(em);
+ closeEntityManager(em);
+
+ em = createEntityManager();
+
+ Query query = em.createQuery(jpqlQueryString);
+ query.setParameter("jigsawId", jigsawTestData.getId());
+ query.setParameter("jigsawPieceId", jigsawPieceTestData.getId());
+
+ if (checkCacheOnly) {
+ query.setHint(QueryHints.CACHE_USAGE, CacheUsage.CheckCacheOnly);
+ }
+
+ JigsawPiece resultPiece = (JigsawPiece) query.getSingleResult();
+ assertNotNull("Query Entity should not be null", resultPiece);
+ Jigsaw resultJigsaw = resultPiece.getJigsaw();
+ assertNotNull("Queried Entity's parent reference should not be null", resultJigsaw);
+ assertEquals("Queried Entity should have the same id", jigsawPieceTestData.getId(), resultPiece.getId());
+ assertEquals("Queried Entity should reference the same parent Entity", jigsawTestData.getId(), resultJigsaw.getId());
+ } finally {
+ if (jigsawTestData != null) {
+ beginTransaction(em);
+ Jigsaw jigsawToRemove = em.find(Jigsaw.class, jigsawTestData.getId());
+ em.remove(jigsawToRemove);
+ for (JigsawPiece piece : jigsawToRemove.getPieces()) {
+ em.remove(piece);
+ }
+ commitTransaction(em);
+ }
+ closeEntityManager(em);
+ }
+ }
+
}