| Summary: | Conforming query arbitrarily returns first object in unit of work when it depends on a non-triggered lazy attribute | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Product: | z_Archived | Reporter: | Mark Wolochuk <mwolochuk> | ||||||||
| Component: | Eclipselink | Assignee: | Nobody - feel free to take it <nobody> | ||||||||
| Status: | RESOLVED FIXED | QA Contact: | |||||||||
| Severity: | normal | ||||||||||
| Priority: | P2 | CC: | david.minsky, kamal.tom, tom.ware, zivl | ||||||||
| Version: | unspecified | ||||||||||
| Target Milestone: | --- | ||||||||||
| Hardware: | All | ||||||||||
| OS: | All | ||||||||||
| Whiteboard: | |||||||||||
| Attachments: |
|
||||||||||
Setting target and priority. See the following page for details of the meanings of these fields: http://wiki.eclipse.org/EclipseLink/Development/Bugs/Guidelines Getting the same issue on version 2.2.0. Returning the first object in UnitOfWork ignoring query parameters. Same scenario works on Toplink 10.1.3. Created attachment 242993 [details]
Potential fix & testing
The incorrect results are returned because in ExpressionQueryMechanism checkCacheForObject(), the in memory query indirection policy is switched to InMemoryQueryIndirectionPolicy.SHOULD_IGNORE_EXCEPTION_RETURN_CONFORMED
For a OneToOne conforming query involving untriggered ValueHolders, when an untriggered ValueHolder is encountered, an exception is thrown, which can return potentially incorrect results.
The solution is to set the in memory query indirection policy to InMemoryQueryIndirectionPolicy.SHOULD_IGNORE_EXCEPTION_RETURN_NOT_CONFORMED, so that when an exception is encountered whilst conforming, "not conformed" is returned. Perhaps this was a typo.
Created attachment 242994 [details]
Potential fix & testing v2
Created attachment 243053 [details]
Fix & overhauled testing
Overhauled the testcase logic
Checked into master (2.6.0) at: 95581d385f0454354caf1dc603a14cf94cddd9fa Comment on attachment 243053 [details]
Fix & overhauled testing
An important condition for the bug is uninstantiated value holder. The overhauled testcase does not test a such situation.
The reason is the second line in method 'test' (sampleEmployee.getManager()) instantiates the queried value holder before a query is executed.
By design, EclipseLink does not trigger ValueHolders when conforming, unless the query is configured to do so (i.e. a conforming query has the InMemoryQueryIndirectionPolicy.SHOULD_TRIGGER_INDIRECTION policy set). This bug concerns the (incorrect) arbitrary return of an unconformed object within the IdentityMap. Apologies, I misread: Lines 46 & 47 in setup() deliberately triggers the lazy-load for employee->manager, as well as validating the test data. Afterwards, the IdentityMaps are cleared, and only the "id" and "lastName" attributes are accessed on employee/manager. A ReportQuery could have been used instead to obtain just the "id" & "lastName" attributes required for the test. IMHO, Mark is right. If we want the query result meets the query criteria we have to trigger valueholder. Instaed of getting results which does not respect given criteria we should get exception. Only in special situations (new object or object already checked in parent session or DB) without queried valueholder instantiation we are able to infer whether an object conforms to the criteria or not. Moreover generally, we should not implictly ignore exceptions. I would expect solution like this: if (policyToUse != SHOULD_THROW_INDIRECTION_EXCEPTION) policyToUse = SHOULD_TRIGGER_INDIRECTION; For projects in my company we have been using Eclipselink with such workaround for years. (In reply to David Minsky from comment #9) > Apologies, I misread: > > Lines 46 & 47 in setup() deliberately triggers the lazy-load for > employee->manager, as well as validating the test data. Afterwards, the > IdentityMaps are cleared, and only the "id" and "lastName" attributes are > accessed on employee/manager. A ReportQuery could have been used instead to > obtain just the "id" & "lastName" attributes required for the test. I'm sorry, I misread - too clever for me. :) The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink |
When a conforming ReadObject query is used in conjunction with a lazy one-to-one attribute that has not yet been triggered, EclipseLink arbitrarily returns the first object in its Unit Of Work instead of the one that matches the query. Steps to reproduce: Create two entities, Parent and Child. Child should be an attribute of parent, mapped as one-to-one, and uses ValueHolderInterface indirection. Save two instances of each, parent1 with child1, and parent2 with child2. The following method illustrates the type of query: public Parent findParentByChildId(Integer childId) { ExpressionBuilder fieldExpression = new ExpressionBuilder(); Expression exp = fieldExpression.get("child").get("id").equal(childId); ReadObjectQuery q = new ReadObjectQuery(Parent.class, exp); q.conformResultsInUnitOfWork(); return (Parent) getUnitOfWork().executeQuery(q); } The following test reproduces the problem: @Test public void testConformingQueryBug() { Parent parent1 = findParentByChildId(CHILD1_ID); assertNotNull("parent1 found", parent1); assertEquals("parent1 id", PARENT1_ID, parent1.getId()); Parent parent2 = findParentByChildId(CHILD2_ID); assertNotNull("parent2 found", parent2); assertEquals("parent2 id", PARENT2_ID, parent2.getId()); // FAILS! PARENT1 WAS FOUND INSTEAD OF PARENT2! } I believe the expected behavior is that EclipseLink should throw an exception because the lazy load has not been triggered. A workaround is to set the query to trigger indirection: q.setInMemoryQueryIndirectionPolicyState(InMemoryQueryIndirectionPolicy.SHOULD_TRIGGER_INDIRECTION);