Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 370474 - in a joined inheritance hierarchy, base class OneToMany relationship, query using join fetch works once then fails
Summary: in a joined inheritance hierarchy, base class OneToMany relationship, query u...
Status: CLOSED FIXED
Alias: None
Product: z_Archived
Classification: Eclipse Foundation
Component: Eclipselink (show other bugs)
Version: unspecified   Edit
Hardware: PC Windows 7
: P2 normal (vote)
Target Milestone: ---   Edit
Assignee: Andrei Ilitchev CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-02-02 12:59 EST by Tony Rozga CLA
Modified: 2022-06-09 10:03 EDT (History)
3 users (show)

See Also:


Attachments
code to reproduce defect 100% of the time (4.44 KB, application/zip)
2012-02-02 13:01 EST, Tony Rozga CLA
no flags Details
Suggested patch (7.26 KB, patch)
2012-04-26 17:27 EDT, Andrei Ilitchev CLA
no flags Details | Diff
Updated patch. (8.86 KB, patch)
2012-04-27 16:56 EDT, Andrei Ilitchev CLA
no flags Details | Diff
patch take 3 (10.11 KB, patch)
2012-04-30 12:35 EDT, Andrei Ilitchev CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Tony Rozga CLA 2012-02-02 12:59:31 EST
Build Identifier: 

I will attach 5 java files that reproduce this defect 100% of the time.
In short:

Model:
AllYourBase----------OneToMany-------Prop
   |
   ^- SubThing extends AllYourBase

Query:
SELECT distinct e 
FROM AllYourBase e JOIN FETCH e.props 
WHERE e.id.id = :p AND e.id.mdrProduct = :q AND e.id.mdrProdInstance = :r

Defect: if statement shouldPrepare is true, running this query once works.  However, subsequent executions always result in NPE:

java.lang.NullPointerException
	at org.eclipse.persistence.descriptors.InheritancePolicy.selectAllRowUsingDefaultMultipleTableSubclassRead(InheritancePolicy.java:1376)
	at org.eclipse.persistence.descriptors.InheritancePolicy.selectAllRowUsingMultipleTableSubclassRead(InheritancePolicy.java:1417)
	at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectAllRows(ExpressionQueryMechanism.java:2567)
	at org.eclipse.persistence.queries.ReadAllQuery.executeObjectLevelReadQuery(ReadAllQuery.java:420)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1081)
	at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:844)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1040)
	at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:392)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1128)
	at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2871)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1516)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1498)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1463)
	at org.eclipse.persistence.internal.jpa.EJBQueryImpl.executeReadQuery(EJBQueryImpl.java:485)
	at org.eclipse.persistence.internal.jpa.EJBQueryImpl.getResultList(EJBQueryImpl.java:742)
	at eclipsebug.BugMain.getItemByIdWithProps(BugMain.java:80)
	at eclipsebug.BugMain.doit(BugMain.java:56)
	at eclipsebug.BugMain.main(BugMain.java:15)

A workaround is to set the query hint eclipselink.prepare=false.
This does not fail in 2.2.x eclipselink lineage, fails in all 2.3.x versions I've tried up to and including 2.3.2

Reproducible: Always

Steps to Reproduce:
1.Run included BugMain.java.  Note it requires a persistence unit named EclipseLinkBug.  I am running against SqlServer 2008 with unit:

 <persistence-unit name="EclipseLinkBug" transaction-type="RESOURCE_LOCAL">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <class>eclipsebug.Prop</class>
    <class>eclipsebug.AllYourBase</class>
    <class>eclipsebug.SubThing</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
      <property name="javax.persistence.jdbc.url" value="jdbc:sqlserver:REMOVED"/>
      <property name="javax.persistence.jdbc.password" value="REMOVED"/>
      <property name="javax.persistence.jdbc.driver" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
      <property name="javax.persistence.jdbc.user" value="REMOVED"/>
      <property name="eclipselink.logging.level" value="FINE"/>
      <property name="eclipselink.canonicalmodel.subpackage" value="EclipseLinkBug"/>
      <property name="eclipselink.jdbc.batch-writing" value="JDBC"/>
      <property name="eclipselink.jdbc.cache-statements" value="false"/>
      <property name="eclipselink.jdbc.cache-statements.size" value="0"/>
      <property name="eclipselink.weaving" value="static"/>
      <property name="eclipselink.ddl-generation.output-mode" value="both"/>
      <property name="eclipselink.ddl-generation" value="create-tables"/>
    </properties>
  </persistence-unit>


2.
3.
Comment 1 Tony Rozga CLA 2012-02-02 13:01:28 EST
Created attachment 210466 [details]
code to reproduce defect 100% of the time

see BugMain.java
Comment 2 Tom Ware CLA 2012-02-17 10:46:25 EST
Setting target and priority.  See the following page for the meanings of these fields:

http://wiki.eclipse.org/EclipseLink/Development/Bugs/Guidelines

Community: Please vote for this bug if it is important to you.  Votes are one of the main criteria we use to determine which bugs to fix next.
Comment 3 Andrei Ilitchev CLA 2012-04-26 17:27:21 EDT
Created attachment 214642 [details]
Suggested patch

The problem arises because calls are shared by the cloned queries but JoinedAttributeManager are not.

When the query executes it is cloned and the clone is actually executed. 
On the first execution of ExpressionQueryMechanism.selectAllRowsFromConcreteTable there is no call corresponding to the reference class, so new call is created and cached in query.getConcreteSubclassCalls(), which is shared between the clone and the original query; also buildConcreteSelectStatement, which sets into JoinedAttributeManager.computeJoiningMappingIndexes for the clone query (but NOT into the original).
When the second cloned query executed  ExpressionQueryMechanism.selectAllRowsFromConcreteTable has the cached call, so it no longer calls buildConcreteSelectStatement - but that means JoinedAttributeManager.computeJoiningMappingIndexes is left null -> NPE.

The suggested solution is to follow with JoiningMappingIndexes the same pattern as with getConcreteSubclassCalls - keep them in a Map which is shared between all query's clones, keyed by query reference class.

While implementing that I've noticed that getConcreteSubclassCalls is currently nullified on each  query.setIsPrepared(false) - which happens on setting any new reference class unequal to the original one. Of course that completely defies the purpose of the whole arrangement: to keep the cached calls (and now joiningMappingIndexes, too) beyond setting of a new class.
Therefore I've moved nullification of these two maps form query.setIsPrepared(false) to setIsPrePrepared(false).

Test: EntityManagerJUnitTestSuite.testInheritanceFetchJoinSecondCall
Comment 4 Andrei Ilitchev CLA 2012-04-27 16:56:10 EDT
Created attachment 214742 [details]
Updated patch.

Moved nullifying of concreteSubclass maps from setIsPrePrepared(false) back to setIsPrepared(false), define a new method setIsPreparedKeepingSubclassData to keep concreteSubclass maps when setting reference class and descriptor.
Comment 5 Andrei Ilitchev CLA 2012-04-30 12:35:51 EDT
Created attachment 214810 [details]
patch take 3

Following James's review:
- set joined mapping indexes before setting call to avoid concurrency problem (the same NPE was still possible if call set before joined mapping indexes);
- selectOneRowsromConcreteTable method follows the pattern of selectAllRowsFromConcreteTable method.
Comment 6 Andrei Ilitchev CLA 2012-04-30 13:30:10 EDT
Checked the patch into both trunk (2.4) and 2.3.3. Reviewed by James.
Comment 7 Eclipse Webmaster CLA 2022-06-09 10:03:51 EDT
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink