Bug 436871 - Fixed NPE in checkForCustomQuery (LRG shall pass now)
Signed-off-by: Tomas Kraus <tomas.kraus@oracle.com>
Reviewed-by: Lukas Jungmann <lukas.jungmann@oracle.com>, Martin Grebac <martin.grebac@oracle.com>
diff --git a/foundation/eclipselink.core.test/src/org/eclipse/persistence/testing/tests/queries/ObjectLevelReadQueryTest.java b/foundation/eclipselink.core.test/src/org/eclipse/persistence/testing/tests/queries/ObjectLevelReadQueryTest.java
new file mode 100644
index 0000000..833fbca
--- /dev/null
+++ b/foundation/eclipselink.core.test/src/org/eclipse/persistence/testing/tests/queries/ObjectLevelReadQueryTest.java
@@ -0,0 +1,356 @@
+/*******************************************************************************
+ * Copyright (c) 2015 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:
+ * 04/20/2015-2.7 Tomas Kraus - Concurrency test to trigger NPE in
+ * checkForCustomQuery(AbstractSession, AbstractRecord).
+ ******************************************************************************/
+package org.eclipse.persistence.testing.tests.queries;
+
+import static org.eclipse.persistence.logging.SessionLog.INFO;
+import static org.eclipse.persistence.logging.SessionLog.WARNING;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.eclipse.persistence.internal.sessions.AbstractRecord;
+import org.eclipse.persistence.internal.sessions.AbstractSession;
+import org.eclipse.persistence.logging.SessionLog;
+import org.eclipse.persistence.queries.DatabaseQuery;
+import org.eclipse.persistence.queries.ObjectLevelReadQuery;
+import org.eclipse.persistence.queries.ReadAllQuery;
+import org.eclipse.persistence.queries.ReadObjectQuery;
+import org.eclipse.persistence.queries.SQLCall;
+import org.eclipse.persistence.sessions.Session;
+import org.eclipse.persistence.testing.framework.TestCase;
+import org.eclipse.persistence.testing.models.employee.domain.Employee;
+
+/**
+ * Test read queries using objects.
+ */
+public abstract class ObjectLevelReadQueryTest extends TestCase {
+
+ /** Fail message for NPE in checkForCustomQuery method. */
+ private static final String FAIL_NPE
+ = "NullPointerException thrown in checkForCustomQuery(AbstractSession, AbstractRecord)";
+
+ /** Logger used in test. */
+ protected SessionLog log;
+
+ /** Current database session. */
+ protected Session session;
+
+ /** Some entity to be used in query. */
+ protected final Class entity = Employee.class;
+
+ /** Some SQL call to be used in query. */
+ protected final SQLCall call = new SQLCall("SELECT t0.EMP_ID FROM EMPLOYEE t0");
+
+ /**
+ * Creates an instance of jUnit tests for read queries using objects.
+ * @param name jUnit test name.
+ */
+ public ObjectLevelReadQueryTest() {
+ super();
+ }
+
+ /**
+ * Creates an instance of jUnit tests for read queries using objects.
+ * @param name jUnit test name.
+ */
+ public ObjectLevelReadQueryTest(final String name) {
+ super();
+ setName(name);
+ }
+
+ /**
+ * Initialize this test suite.
+ */
+ public void setup() {
+ session = getSession();
+ log = session.getSessionLog();
+ }
+
+ /**
+ * Clean this test suite.
+ */
+ public void reset() {
+ session = null;
+ log = null;
+ }
+
+ /**
+ * {@link TestCase#test()} method overwritten as public to avoid failures.
+ */
+ @Override
+ public abstract void test() throws Throwable;
+
+ /**
+ * {@code [ObjectLevelReadQuery].isCustomQueryUsed} value changer
+ * for {@link #testCheckForCustomQueryRaceConditions()}.
+ */
+ private static final class ValueChanger extends Thread {
+
+ /** Logger used in test. */
+ private final SessionLog log;
+
+ /** Query where to change {@code isCustomQueryUsed} value. */
+ private final ObjectLevelReadQuery query;
+
+ /** Instance used for synchronization. */
+ private final Object mutex;
+
+ /** [ObjectLevelReadQuery].isCustomQueryUsed} field. */
+ private final Field isCustomQueryUsed;
+
+ /** Thread execution flag. */
+ private boolean execute;
+
+ /**
+ * Creates an instance of value changer thread.
+ * @param objectLevelReadQuery Query object where {@code isCustomQueryUsed} value will be changed.
+ * @param syncMutex Instance used for synchronization.
+ * @param sessionLog Logger used in test.
+ */
+ private ValueChanger(final ObjectLevelReadQuery objectLevelReadQuery, final Object syncMutex,
+ final SessionLog sessionLog) {
+ log = sessionLog;
+ // Calling run() directly will do nothing.
+ query = objectLevelReadQuery;
+ mutex = syncMutex;
+ execute = false;
+ // Placeholder for initialization.
+ Field isCustomQueryUsedLocal;
+ try {
+ isCustomQueryUsedLocal = DatabaseQuery.class.getDeclaredField("isCustomQueryUsed");
+ } catch (SecurityException e) {
+ log.logThrowable(WARNING, e);
+ isCustomQueryUsedLocal = null;
+ } catch (NoSuchFieldException e) {
+ log.logThrowable(WARNING, e);
+ isCustomQueryUsedLocal = null;
+ }
+ if (isCustomQueryUsedLocal != null) {
+ try {
+ isCustomQueryUsedLocal.setAccessible(true);
+ } catch (SecurityException e) {
+ log.logThrowable(WARNING, e);
+ isCustomQueryUsedLocal = null;
+ }
+ }
+ isCustomQueryUsed = isCustomQueryUsedLocal;
+ }
+
+ /**
+ * Check if value changer initialization was successful.
+ * @return Value of {@code true} when {@code isCustomQueryUsed} field is available or {@code false} otherwise.
+ */
+ private boolean isOk() {
+ return isCustomQueryUsed != null;
+ }
+
+ /**
+ * Start thread execution.
+ */
+ public void start() {
+ execute = true;
+ super.start();
+ }
+
+ /**
+ * Request thread to finish.
+ */
+ public void finish() {
+ execute = false;
+ }
+
+ /**
+ * Thread code to be executed.
+ */
+ @Override
+ public void run() {
+ synchronized(mutex) {
+ mutex.notify();
+ }
+ while(execute) {
+ try {
+ isCustomQueryUsed.set(query, Boolean.TRUE);
+ isCustomQueryUsed.set(query, (Boolean)null);
+ } catch (IllegalArgumentException e) {
+ log.logThrowable(WARNING, e);
+ execute = false;
+ } catch (IllegalAccessException e) {
+ log.logThrowable(WARNING, e);
+ execute = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Get {@code [ObjectLevelReadQuery].checkForCustomQuery(AbstractSession, AbstractRecord)} method accessor.
+ * @param c Class where to search for method.
+ * @return {@code [ObjectLevelReadQuery].checkForCustomQuery(AbstractSession, AbstractRecord)} method accessor.
+ */
+ protected Method getCheckForCustomQueryMethod(final Class c) {
+ Method method;
+ try {
+ method = c.getDeclaredMethod(
+ "checkForCustomQuery", AbstractSession.class, AbstractRecord.class);
+ method.setAccessible(true);
+ } catch (NoSuchMethodException e) {
+ log.logThrowable(WARNING, e);
+ method = null;
+ } catch (SecurityException e) {
+ log.logThrowable(WARNING, e);
+ method = null;
+ }
+ return method;
+ }
+
+ /**
+ * Call {@code [ObjectLevelReadQuery].checkForCustomQuery(AbstractSession, AbstractRecord)} method.
+ * @param instance ObjectLevelReadQuery instance on which method is called.
+ * @param mathod {@code [ObjectLevelReadQuery].checkForCustomQuery(AbstractSession, AbstractRecord)}
+ * method accessor.
+ * @throws Throwable when any exception except NPE in invoked method occurs.
+ */
+ protected DatabaseQuery callCheckForCustomQueryMethod(final Object instance, final Method method) throws Throwable {
+ DatabaseQuery databaseQuery;
+ try {
+ databaseQuery = (DatabaseQuery)method.invoke(instance, session, (AbstractRecord)null);
+ } catch (InvocationTargetException e) {
+ if (e.getTargetException() instanceof NullPointerException) {
+ log.log(WARNING, FAIL_NPE);
+ e.printStackTrace();
+ fail(FAIL_NPE);
+ }
+ log.logThrowable(WARNING, e);
+ throw e;
+ } catch (Throwable e) {
+ log.logThrowable(WARNING, e);
+ e.printStackTrace();
+ throw e;
+ }
+ return databaseQuery;
+ }
+
+ /**
+ * jUnit test to verify NPE in {@code checkForCustomQuery} method in {@link ReadAllQuery} class.
+ */
+ public static final class CustomQueryRaceConditionsInReadAllQueryTest extends ObjectLevelReadQueryTest {
+
+ /**
+ * Creates an instance of jUnit test.
+ */
+ public CustomQueryRaceConditionsInReadAllQueryTest() {
+ super();
+ }
+
+ /**
+ * Creates an instance of jUnit test.
+ * @param name jUnit test name.
+ */
+ public CustomQueryRaceConditionsInReadAllQueryTest(final String name) {
+ super(name);
+ }
+
+ /**
+ * Test {@code [ReadAllQuery].checkForCustomQuery(AbstractSession, AbstractRecord)}
+ * when {@code isCustomQueryUsed} is changed to null during query execution.
+ * Reproduction scenario for bug# 436871 which works with high enough probability.
+ * @throws Throwable when any exception except NPE in checkForCustomQuery occurs.
+ */
+ @Override
+ public void test() throws Throwable {
+ log.log(INFO, "Running checkCustomQueryRaceConditionsInReadAllQuery test.");
+ final Method checkForCustomQuery = getCheckForCustomQueryMethod(ReadAllQuery.class);
+ final ReadAllQuery query = new ReadAllQuery(entity, call);
+ final Object mutex = new Object();
+ final ValueChanger changer = new ValueChanger(query, mutex, log);
+ assertTrue("Initialization of isCustomQueryUsed property value changer failed.", changer.isOk());
+ try {
+ synchronized (mutex) {
+ changer.start();
+ mutex.wait();
+ }
+ } catch (InterruptedException e) {
+ log.logThrowable(WARNING, e);
+ }
+ query.setReferenceClass(entity);
+ query.setDescriptor(session.getDescriptor(entity));
+ // 100 iterations is enough to get exception in almost 100% of tests.
+ for (int i = 0; i < 100; i++)
+ callCheckForCustomQueryMethod(query, checkForCustomQuery);
+ changer.finish();
+ try {
+ changer.join();
+ } catch (InterruptedException e) {
+ log.logThrowable(WARNING, e);
+ }
+ }
+ }
+
+ /**
+ * jUnit test to verify NPE in {@code checkForCustomQuery} method in {@link ReadObjectQuery} class.
+ */
+ public static final class CustomQueryRaceConditionsInReadObjectQueryTest extends ObjectLevelReadQueryTest {
+
+ /**
+ * Creates an instance of jUnit test.
+ */
+ public CustomQueryRaceConditionsInReadObjectQueryTest() {
+ super();
+ }
+
+ /**
+ * Creates an instance of jUnit test.
+ * @param name jUnit test name.
+ */
+ public CustomQueryRaceConditionsInReadObjectQueryTest(final String name) {
+ super(name);
+ }
+
+ /**
+ * Test {@code [ReadObjectQuery].checkForCustomQuery(AbstractSession, AbstractRecord)}
+ * when {@code isCustomQueryUsed} is changed to null during query execution.
+ * Reproduction scenario for bug# 436871 which works with high enough probability.
+ * @throws Throwable when any exception except NPE in checkForCustomQuery occurs.
+ */
+ @Override
+ public void test() throws Throwable {
+ log.log(INFO, "Running checkCustomQueryRaceConditionsInReadObjectQuery test.");
+ final Method checkForCustomQuery = getCheckForCustomQueryMethod(ReadObjectQuery.class);
+ final ReadObjectQuery query = new ReadObjectQuery(entity, call);
+ final Object mutex = new Object();
+ final ValueChanger changer = new ValueChanger(query, mutex, log);
+ assertTrue("Initialization of isCustomQueryUsed property value changer failed.", changer.isOk());
+ try {
+ synchronized (mutex) {
+ changer.start();
+ mutex.wait();
+ }
+ } catch (InterruptedException e) {
+ log.logThrowable(WARNING, e);
+ }
+ query.setReferenceClass(entity);
+ query.setDescriptor(session.getDescriptor(entity));
+ // 100 iterations is enough to get exception in almost 100% of tests.
+ for (int i = 0; i < 100; i++)
+ callCheckForCustomQueryMethod(query, checkForCustomQuery);
+ changer.finish();
+ try {
+ changer.join();
+ } catch (InterruptedException e) {
+ log.logThrowable(WARNING, e);
+ }
+ }
+ }
+}
diff --git a/foundation/eclipselink.core.test/src/org/eclipse/persistence/testing/tests/queries/QueryFrameworkTestSuite.java b/foundation/eclipselink.core.test/src/org/eclipse/persistence/testing/tests/queries/QueryFrameworkTestSuite.java
index eb86097..510657d 100644
--- a/foundation/eclipselink.core.test/src/org/eclipse/persistence/testing/tests/queries/QueryFrameworkTestSuite.java
+++ b/foundation/eclipselink.core.test/src/org/eclipse/persistence/testing/tests/queries/QueryFrameworkTestSuite.java
@@ -1,30 +1,52 @@
-/*******************************************************************************
- * Copyright (c) 1998, 2013 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.
+/*******************************************************************************
+ * Copyright (c) 1998, 2015 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:
* Oracle - initial API and implementation from Oracle TopLink
- ******************************************************************************/
-package org.eclipse.persistence.testing.tests.queries;
-
-import java.util.*;
-import org.eclipse.persistence.testing.models.employee.domain.*;
-import org.eclipse.persistence.testing.models.employee.domain.Project;
-import org.eclipse.persistence.sessions.*;
-import org.eclipse.persistence.expressions.*;
-import org.eclipse.persistence.internal.sessions.*;
-import org.eclipse.persistence.queries.*;
-import org.eclipse.persistence.sessions.Record;
-import org.eclipse.persistence.testing.framework.*;
-import org.eclipse.persistence.testing.tests.clientserver.*;
-import org.eclipse.persistence.tools.schemaframework.PopulationManager;
-
-public class QueryFrameworkTestSuite extends TestSuite {
+ ******************************************************************************/
+package org.eclipse.persistence.testing.tests.queries;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.Vector;
+
+import org.eclipse.persistence.expressions.ExpressionBuilder;
+import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
+import org.eclipse.persistence.queries.ComplexQueryResult;
+import org.eclipse.persistence.queries.Cursor;
+import org.eclipse.persistence.queries.DatabaseQuery;
+import org.eclipse.persistence.queries.MethodBaseQueryRedirector;
+import org.eclipse.persistence.queries.ObjectLevelReadQuery;
+import org.eclipse.persistence.queries.QueryRedirector;
+import org.eclipse.persistence.queries.ReadAllQuery;
+import org.eclipse.persistence.queries.ReadObjectQuery;
+import org.eclipse.persistence.queries.ReportQuery;
+import org.eclipse.persistence.queries.ScrollableCursorPolicy;
+import org.eclipse.persistence.sessions.DatabaseRecord;
+import org.eclipse.persistence.sessions.Record;
+import org.eclipse.persistence.sessions.UnitOfWork;
+import org.eclipse.persistence.testing.framework.TestCase;
+import org.eclipse.persistence.testing.framework.TestSuite;
+import org.eclipse.persistence.testing.models.employee.domain.Employee;
+import org.eclipse.persistence.testing.models.employee.domain.EmployeePopulator;
+import org.eclipse.persistence.testing.models.employee.domain.LargeProject;
+import org.eclipse.persistence.testing.models.employee.domain.Project;
+import org.eclipse.persistence.testing.tests.clientserver.ServerSessionTestAdapter;
+import org.eclipse.persistence.tools.schemaframework.PopulationManager;
+
+public class QueryFrameworkTestSuite extends TestSuite {
public QueryFrameworkTestSuite() {
setDescription("This suite tests all of the functionality of the query framework.");
}
@@ -34,18 +56,20 @@
setDescription("This suite tests all of the functionality of the query framework.");
}
- public void addTests() {
- addSRGTests();
- addTest(new QueryTimeoutTest());
- //Add new tests here, if any.
- addTest(new ServerSessionTestAdapter(new PessimisticLockNoLockJoinedTest()));
- addTest(new ReadAllNoDistinctTest());
- addTest(new PartialAttributeTestWithJoinAttribute());
- addTest(new PartialAttributeDistinctOrderByTest());
- addTest(new FourPartialAttributeTestsWithJoinAttribute());
- addTest(buildReadOnlyQueryTest());
- addTest(buildGetSQLTest());
- addTest(buildJoinSubclassesQueryTest());
+ public void addTests() {
+ addSRGTests();
+ addTest(new QueryTimeoutTest());
+ //Add new tests here, if any.
+ addTest(new ServerSessionTestAdapter(new PessimisticLockNoLockJoinedTest()));
+ addTest(new ReadAllNoDistinctTest());
+ addTest(new ObjectLevelReadQueryTest.CustomQueryRaceConditionsInReadAllQueryTest());
+ addTest(new ObjectLevelReadQueryTest.CustomQueryRaceConditionsInReadObjectQueryTest());
+ addTest(new PartialAttributeTestWithJoinAttribute());
+ addTest(new PartialAttributeDistinctOrderByTest());
+ addTest(new FourPartialAttributeTestsWithJoinAttribute());
+ addTest(buildReadOnlyQueryTest());
+ addTest(buildGetSQLTest());
+ addTest(buildJoinSubclassesQueryTest());
addTest(buildRecordTest());
// Bug 6450867 - TOPLINK-6069 ON READOBJECTQUERY ON ENTITY USING MULTIPLE TABLE MAPPING
addTest(new ConformResultsWithMultitableAndJoiningTest());
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/ReadAllQuery.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/ReadAllQuery.java
index 79ed566..733bb4e 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/ReadAllQuery.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/ReadAllQuery.java
@@ -204,32 +204,36 @@
* Check to see if a custom query should be used for this query.
* This is done before the query is copied and prepared/executed.
* null means there is none.
- */
- @Override
- protected DatabaseQuery checkForCustomQuery(AbstractSession session, AbstractRecord translationRow) {
- checkDescriptor(session);
-
- // Check if user defined a custom query.
- if (isCustomQueryUsed() == null) {
- setIsCustomQueryUsed((!isUserDefined()) && isExpressionQuery() && (getSelectionCriteria() == null) && isDefaultPropertiesQuery() && (!hasOrderByExpressions()) && (this.descriptor.getQueryManager().hasReadAllQuery()));
- }
- if (isCustomQueryUsed().booleanValue()) {
- ReadAllQuery customQuery = this.descriptor.getQueryManager().getReadAllQuery();
- if (this.accessors != null) {
- customQuery = (ReadAllQuery) customQuery.clone();
- customQuery.setIsExecutionClone(true);
- customQuery.setAccessors(this.accessors);
- }
- return customQuery;
- } else {
- return null;
- }
- }
-
- /**
- * INTERNAL:
- * Clone the query.
- */
+ */
+ @Override
+ protected DatabaseQuery checkForCustomQuery(AbstractSession session, AbstractRecord translationRow) {
+ Boolean useCustomQuery = isCustomQueryUsed;
+
+ checkDescriptor(session);
+ // Check if user defined a custom query.
+ if (useCustomQuery == null) {
+ setIsCustomQueryUsed((!isUserDefined()) && isExpressionQuery() && (getSelectionCriteria() == null) && isDefaultPropertiesQuery() && (!hasOrderByExpressions()) && (this.descriptor.getQueryManager().hasReadAllQuery()));
+ // Value of isCustomQueryUsed is updated by setIsCustomQueryUsed method.
+ useCustomQuery = isCustomQueryUsed;
+ }
+ if (useCustomQuery != null && useCustomQuery.booleanValue()) {
+ ReadAllQuery customQuery = this.descriptor.getQueryManager().getReadAllQuery();
+ if (this.accessors != null) {
+ customQuery = (ReadAllQuery) customQuery.clone();
+ customQuery.setIsExecutionClone(true);
+ customQuery.setAccessors(this.accessors);
+ }
+ isCustomQueryUsed = useCustomQuery;
+ return customQuery;
+ }
+ isCustomQueryUsed = useCustomQuery;
+ return null;
+ }
+
+ /**
+ * INTERNAL:
+ * Clone the query.
+ */
@Override
public Object clone() {
ReadAllQuery cloneQuery = (ReadAllQuery)super.clone();
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/ReadObjectQuery.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/ReadObjectQuery.java
index fed5d30..0abfb37 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/ReadObjectQuery.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/ReadObjectQuery.java
@@ -1,8 +1,8 @@
-/*******************************************************************************
- * Copyright (c) 1998, 2013 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.
+/*******************************************************************************
+ * 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.
@@ -30,13 +30,12 @@
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.ResultSetRecord;
import org.eclipse.persistence.internal.sessions.SimpleResultSetRecord;
-import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
-import org.eclipse.persistence.internal.helper.*;
-import org.eclipse.persistence.descriptors.ClassDescriptor;
-import org.eclipse.persistence.descriptors.DescriptorQueryManager;
-import org.eclipse.persistence.exceptions.*;
-import org.eclipse.persistence.expressions.*;
-import org.eclipse.persistence.sessions.DatabaseRecord;
+import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
+import org.eclipse.persistence.internal.helper.*;
+import org.eclipse.persistence.descriptors.ClassDescriptor;
+import org.eclipse.persistence.exceptions.*;
+import org.eclipse.persistence.expressions.*;
+import org.eclipse.persistence.sessions.DatabaseRecord;
import org.eclipse.persistence.sessions.SessionProfiler;
import org.eclipse.persistence.sessions.remote.*;
import org.eclipse.persistence.tools.profiler.QueryMonitor;
@@ -295,62 +294,58 @@
* INTERNAL:
* Check to see if a custom query should be used for this query.
* This is done before the query is copied and prepared/executed.
- * null means there is none.
- */
- protected DatabaseQuery checkForCustomQuery(AbstractSession session, AbstractRecord translationRow) {
- if (this.descriptor == null) {
- checkDescriptor(session);
- }
-
- if (this.isCustomQueryUsed == null) {
- // Check if user defined a custom query in the query manager.
- if (!this.isUserDefined) {
- if (!isCallQuery()) {
- DescriptorQueryManager descriptorQueryManager = this.descriptor.getQueryManager();
-
- // By default all descriptors have a custom ("static") read-object query.
- // This allows the read-object query and SQL to be prepare once.
- if (descriptorQueryManager.hasReadObjectQuery()) {
- // If the query require special SQL generation or execution do not use the static read object query.
- // PERF: the read-object query should always be static to ensure no regeneration of SQL.
- if ((!hasJoining() || !this.joinedAttributeManager.hasJoinedAttributeExpressions()) && (!hasPartialAttributeExpressions()) && (redirector == null) && !doNotRedirect && (!hasAsOfClause()) && (!hasNonDefaultFetchGroup())
- && (this.shouldUseSerializedObjectPolicy == shouldUseSerializedObjectPolicyDefault)
- && this.wasDefaultLockMode && (shouldBindAllParameters == null) && (this.hintString == null)) {
- if ((this.selectionId != null) || (this.selectionObject != null)) {// Must be primary key.
- this.isCustomQueryUsed = true;
- } else {
- Expression selectionCriteria = getSelectionCriteria();
- if (selectionCriteria != null) {
- AbstractRecord primaryKeyRow = this.descriptor.getObjectBuilder().extractPrimaryKeyRowFromExpression(selectionCriteria, translationRow, session);
- // Only execute the query if the selection criteria has the primary key fields set
- if (primaryKeyRow != null) {
- this.isCustomQueryUsed = true;
- }
- }
- }
- }
- }
- }
- }
- if (this.isCustomQueryUsed == null) {
- this.isCustomQueryUsed = false;
- }
- }
-
- if (this.isCustomQueryUsed.booleanValue()) {
- ReadObjectQuery customQuery = this.descriptor.getQueryManager().getReadObjectQuery();
- if (this.accessors != null) {
- customQuery = (ReadObjectQuery) customQuery.clone();
- customQuery.setIsExecutionClone(true);
- customQuery.setAccessors(this.accessors);
- }
- return customQuery;
- } else {
- return null;
- }
- }
-
- /**
+ * null means there is none.
+ */
+ protected DatabaseQuery checkForCustomQuery(AbstractSession session, AbstractRecord translationRow) {
+ Boolean useCustomQuery = isCustomQueryUsed;
+
+ checkDescriptor(session);
+ if (useCustomQuery == null) {
+ // Check if user defined a custom query in the query manager.
+ if (!this.isUserDefined) {
+ if (!isCallQuery()
+ // By default all descriptors have a custom ("static") read-object query.
+ // This allows the read-object query and SQL to be prepare once.
+ && this.descriptor.getQueryManager().hasReadObjectQuery()) {
+ // If the query require special SQL generation or execution do not use the static read object query.
+ // PERF: the read-object query should always be static to ensure no regeneration of SQL.
+ if ((!hasJoining() || !this.joinedAttributeManager.hasJoinedAttributeExpressions()) && (!hasPartialAttributeExpressions())
+ && (redirector == null) && !doNotRedirect && (!hasAsOfClause()) && (!hasNonDefaultFetchGroup())
+ && (this.shouldUseSerializedObjectPolicy == shouldUseSerializedObjectPolicyDefault)
+ && this.wasDefaultLockMode && (shouldBindAllParameters == null) && (this.hintString == null)) {
+ if ((this.selectionId != null) || (this.selectionObject != null)) {// Must be primary key.
+ useCustomQuery = Boolean.TRUE;
+ } else {
+ Expression selectionCriteria = getSelectionCriteria();
+ if (selectionCriteria != null) {
+ AbstractRecord primaryKeyRow = this.descriptor.getObjectBuilder().extractPrimaryKeyRowFromExpression(selectionCriteria, translationRow, session);
+ // Only execute the query if the selection criteria has the primary key fields set
+ if (primaryKeyRow != null) {
+ useCustomQuery = Boolean.TRUE;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //#436871 - attempt to limit cases for race-condition
+ if (useCustomQuery != null && useCustomQuery.booleanValue()) {
+ ReadObjectQuery customQuery = this.descriptor.getQueryManager().getReadObjectQuery();
+ if (this.accessors != null) {
+ customQuery = (ReadObjectQuery) customQuery.clone();
+ customQuery.setIsExecutionClone(true);
+ customQuery.setAccessors(this.accessors);
+ }
+ isCustomQueryUsed = useCustomQuery;
+ return customQuery;
+ }
+ isCustomQueryUsed = useCustomQuery;
+ return null;
+ }
+
+ /**
* INTERNAL:
* Conform the result in the UnitOfWork.
*/