Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 325684 - QueryHints.BATCH combined with QueryHints.FETCH_GROUP_LOAD will cause NPE
Summary: QueryHints.BATCH combined with QueryHints.FETCH_GROUP_LOAD will cause NPE
Status: CLOSED FIXED
Alias: None
Product: z_Archived
Classification: Eclipse Foundation
Component: Eclipselink (show other bugs)
Version: unspecified   Edit
Hardware: All All
: P3 major (vote)
Target Milestone: ---   Edit
Assignee: Nobody - feel free to take it CLA
QA Contact:
URL:
Whiteboard: submitted_patch
Keywords:
Depends on:
Blocks:
 
Reported: 2010-09-18 13:44 EDT by Frank Schwarz CLA
Modified: 2022-06-09 10:23 EDT (History)
4 users (show)

See Also:


Attachments
testcase (5.37 KB, application/x-zip)
2010-09-18 13:50 EDT, Frank Schwarz CLA
no flags Details
proposed patch (904 bytes, patch)
2010-09-18 13:58 EDT, Frank Schwarz CLA
peter.krogh: iplog+
Details | Diff
updated patch (8.03 KB, patch)
2010-09-20 15:48 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 Frank Schwarz CLA 2010-09-18 13:44:04 EDT
Build Identifier: Trunk

If you combine QueryHints.FETCH_GROUP_LOAD and QueryHints.BATCH you will get this NPE:

java.lang.NullPointerException
	at org.eclipse.persistence.internal.sessions.AbstractSession$1.iterate(AbstractSession.java:4076)
	at org.eclipse.persistence.internal.descriptors.DescriptorIterator.startIterationOn(DescriptorIterator.java:666)
	at org.eclipse.persistence.internal.sessions.AbstractSession.load(AbstractSession.java:4105)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1087)
	at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:768)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1038)
	at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:383)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1116)
	at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2943)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1301)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1283)
	at org.eclipse.persistence.mappings.CollectionMapping.executeBatchQuery(CollectionMapping.java:842)
	at org.eclipse.persistence.mappings.ForeignReferenceMapping.extractResultFromBatchQuery(ForeignReferenceMapping.java:503)
	at org.eclipse.persistence.mappings.CollectionMapping.extractResultFromBatchQuery(CollectionMapping.java:823)
	at org.eclipse.persistence.internal.indirection.BatchValueHolder.instantiate(BatchValueHolder.java:55)
	at org.eclipse.persistence.internal.indirection.QueryBasedValueHolder.instantiate(QueryBasedValueHolder.java:75)
	at org.eclipse.persistence.internal.indirection.DatabaseValueHolder.getValue(DatabaseValueHolder.java:84)
	at org.eclipse.persistence.internal.indirection.UnitOfWorkValueHolder.instantiateImpl(UnitOfWorkValueHolder.java:160)
	at org.eclipse.persistence.internal.indirection.UnitOfWorkValueHolder.instantiate(UnitOfWorkValueHolder.java:220)
	at org.eclipse.persistence.internal.indirection.DatabaseValueHolder.getValue(DatabaseValueHolder.java:84)
	at org.eclipse.persistence.indirection.IndirectList.buildDelegate(IndirectList.java:237)
	at org.eclipse.persistence.indirection.IndirectList.getDelegate(IndirectList.java:397)
	at org.eclipse.persistence.indirection.IndirectList.size(IndirectList.java:726)
	at org.eclipse.persistence.internal.queries.CollectionContainerPolicy.sizeFor(CollectionContainerPolicy.java:177)
	at org.eclipse.persistence.internal.indirection.TransparentIndirectionPolicy.instantiateObject(TransparentIndirectionPolicy.java:363)
	at org.eclipse.persistence.mappings.ForeignReferenceMapping.instantiateAttribute(ForeignReferenceMapping.java:914)
	at org.eclipse.persistence.internal.sessions.AbstractSession$1.iterate(AbstractSession.java:4090)
	at org.eclipse.persistence.internal.descriptors.DescriptorIterator.startIterationOn(DescriptorIterator.java:666)
	at org.eclipse.persistence.internal.sessions.AbstractSession.load(AbstractSession.java:4102)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1087)
	at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:768)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1038)
	at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:383)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1116)
	at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2943)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1301)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1283)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1257)
	at org.eclipse.persistence.internal.jpa.EJBQueryImpl.executeReadQuery(EJBQueryImpl.java:479)
	at org.eclipse.persistence.internal.jpa.EJBQueryImpl.getResultList(EJBQueryImpl.java:714)
	at user.test.BatchFetchTest.queryWithEagerBatchFetch(BatchFetchTest.java:98)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Source is:
	EntityManager em = emf.createEntityManager();
	FetchGroup childFG = new FetchGroup();
	childFG.addAttribute("name");
		
	FetchGroup parentFG = new FetchGroup();
	parentFG.addAttribute("name");
	parentFG.addAttribute("children", childFG); 
//	parentFG.setShouldLoadAll(true);
		
	TypedQuery<Parent> query = em.createQuery("select p from Parent p", Parent.class);
	query.setHint(QueryHints.FETCH_GROUP, parentFG);
	query.setHint(QueryHints.FETCH_GROUP_LOAD, HintValues.TRUE);
	query.setHint(QueryHints.BATCH, "p.children");
		
	List<Parent> parents = query.getResultList();


Reproducible: Always

Steps to Reproduce:
A JUnit test case will be provided shortly
Comment 1 Frank Schwarz CLA 2010-09-18 13:50:47 EDT
Created attachment 179180 [details]
testcase

A JUnit test case
Comment 2 Frank Schwarz CLA 2010-09-18 13:58:14 EDT
Created attachment 179181 [details]
proposed patch
Comment 3 Andrei Ilitchev CLA 2010-09-20 15:48:18 EDT
Created attachment 179276 [details]
updated patch

The updated patch fixes the same flaw as the original patch, but in a different place: AbstractSession#load should not be passed ComplexQueryResult, it's responsibility of the query to handle ComplexResultQuery case and extract the actual result from it to pass to load method.
Added a test new simpleFetchGroupLoadWithBatch test case to both fieldaccess.fetchgroups.NestedFetchGroupTests and fetchgroups.NestedFetchGroupTests.

Apart from verifying that NPE is no longer thrown the test case verifies that all the loading is done during query.getResultList.

The test case is (almost) exact copy of the one suggested by Frank in e-mail:
Frank Schwarz wrote:
> > 
> > Hi,
> > 
> > I've been investigating the new fetch-group feature(s) of EL 2.1.0 and
> > there is something I'd like to understand:
> > 
> > Lets say you have got "Employee" -- "Project" (many to many)
> > 
> > FetchGroup projectGroup = new FetchGroup();
> > projectGroup.addAttribute("name");
> > 
> > FetchGroup employeeGroup = new FetchGroup();
> > employeeGroup.addAttribute("id");
> > employeeGroup.addAttribute("name");
> > employeeGroup.addAttribute("projects", projectGroup);
> > 
> > TypedQuery<Employee> query = em.createQuery("select e from ...",
> > Employee.class);
> > query.setHint(QueryHints.FETCH_GROUP, employeeGroup);
> > query.setHint(QueryHints.BATCH, "e.projects");
> > 
> > List<Employee> emps = query.getResultList(); // (1)
> > for (Employee e : emps) {
> >   e.getProjekte().size(); // (2)
> > }
> > 
> > You can observe two sql statements. This first one at position (1) to load
> > all employees and the second one at position (2) to load all projects of
> > all previously loaded employees.
> > 
> > I would have expected the second sql statement to occur also at position
> > (1). I thought that this fetch plan was saying: Please ensure that all
> > employees and all their projects are loaded when you return from
> > "getResultList() and load all projects with a separate batch statement."
> > 
> > Would the following statement change the behavior to the one I am
> > expecting?:
> > 
> > employeeGroup.setShouldLoadAll(true);
> > 
> > And, is batch fetching compatible with this setting?
> > 
> > 
> > Thank you for your help,
> > Frank
> > 
> > 
> > PS: It is currently not possible to simply test it, as it will raise a NPE
> > at
> > org.eclipse.persistence.internal.sessions.AbstractSession$1.iterate(AbstractSession.java:4067)
> > 
> >
Comment 4 Andrei Ilitchev CLA 2010-09-21 12:00:08 EDT
Checked into both trunk (2.2) and 2.1.2. Reviewed by James.
Comment 5 Peter Krogh CLA 2010-12-08 14:28:02 EST
Moving ipLog flag to patch
Comment 6 Eclipse Webmaster CLA 2022-06-09 10:23:44 EDT
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink