| /* |
| * Copyright (c) 1998, 2021 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 v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.testing.tests.queries.inmemory; |
| |
| import java.util.Vector; |
| import java.util.Enumeration; |
| |
| import org.eclipse.persistence.queries.*; |
| import org.eclipse.persistence.expressions.*; |
| import org.eclipse.persistence.sessions.*; |
| |
| import org.eclipse.persistence.testing.framework.*; |
| import org.eclipse.persistence.testing.models.employee.domain.*; |
| |
| /** |
| * Test for feature 2612601: conforming without registering in the Unit Of Work. |
| * The test must insure the following things: |
| * -Conforming still works, even though objects are not being registered. |
| * -Objects returned by a conforming without registering query |
| * are in fact not being registered. |
| * -Registered objects never point to unregistered objects, and vice versa. |
| * -An unregistered object can later be safely registered. |
| * |
| * Test plan: |
| * -First execute two queries on the session: all employees whose address is |
| * in Ontario, and the complement. |
| * -Register employee 1 and change his address to outside Ontario. |
| * -Register employee 2's address and change it to outside Ontario. |
| * -Register employee 3 from outside Ontario and change address to Ontario. |
| * |
| * At this point there are three registered addresses, and two registered |
| * employees. |
| * |
| * If the same query is now executed again on the Unit of Work, conforming |
| * without registering, the following results should be expected: |
| * |
| * -employee 1 will be returned from database, but on conform it will be |
| * replaced with its clone, which points to an address outside Ontario. So |
| * it will be dropped. |
| * |
| * -employee 2 will be returned from database, and since it was not registered |
| * will be returned automatically. This is a limitation from bug 2675667: |
| * CONFORMING QUERIES MISS RESULTS NEWLY CONFORMING TO YET UNREGISTERED IN UOW. |
| * |
| * -employee 3 will not be returned from database, but it will be added in |
| * later, as its address does conform. |
| * |
| * -Even though the query was run on a unit of work, no additional employees |
| * or addresses were registered. |
| * |
| * -Though employee 2's cloned address was used to conform, employee 2 does |
| * not point to the cloned address and vice versa. |
| * |
| * Test Limitations: |
| * |
| * -Only a single simple query was tested. Employee to address is a one to |
| * one mapping, and there is only a single join. |
| * |
| * -Employee 2 has an address outside of Ontario, but is returned by the |
| * conforming query. This is a limitation from bug 2675667: |
| * CONFORMING QUERIES MISS RESULTS NEWLY CONFORMING TO YET UNREGISTERED IN UOW. |
| * Better to not work at all then sometimes. |
| */ |
| public class UnitOfWorkConformWithoutRegisteringTest extends TestCase { |
| protected int expected; |
| protected UnitOfWork uow; |
| protected Vector result; |
| protected Object result2; |
| protected Object result3; |
| protected ReadAllQuery query; |
| protected int numberOfOntarioAddresses; |
| protected Employee emigrant; |
| protected Employee emigrant2; |
| protected Employee immigrant; |
| protected Employee outsider2; |
| |
| public UnitOfWorkConformWithoutRegisteringTest() { |
| setDescription("Test that the query conforms to changes but does not register objects."); |
| } |
| |
| @Override |
| public void reset() { |
| uow.release(); |
| getSession().getIdentityMapAccessor().initializeAllIdentityMaps(); |
| } |
| |
| @Override |
| protected void setup() { |
| getSession().getIdentityMapAccessor().initializeAllIdentityMaps(); |
| uow = getSession().acquireUnitOfWork(); |
| Expression expression = new ExpressionBuilder(Employee.class).get("address").get("province").equal("ONT"); |
| |
| Vector ontarioEmployees = getSession().readAllObjects(Employee.class, expression); |
| numberOfOntarioAddresses = ontarioEmployees.size(); |
| |
| expression = new ExpressionBuilder(Employee.class).get("address").get("province").equal("ONT").not(); |
| Vector nonOntarioEmployees = getSession().readAllObjects(Employee.class, expression); |
| |
| // Case 1: Change one employee's address so he/she no longer lives in Ontario, |
| // and register the employee too. |
| Employee insider = (Employee)ontarioEmployees.elementAt(0); |
| this.emigrant = (Employee)uow.registerExistingObject(insider); |
| this.emigrant.getAddress().setProvince("ICN"); |
| |
| // Case 2: Change one employee's address so he/she no longer lives in Ontario, |
| // but only register the address, not the employee. |
| this.emigrant2 = (Employee)ontarioEmployees.elementAt(1); |
| Address incheonAddress = (Address)uow.registerExistingObject(emigrant2.getAddress()); |
| incheonAddress.setProvince("ICN"); |
| |
| // Case 3: |
| // Here is a dilemma. We would like to change one address so that it now |
| // conforms, but its owner must be in the cache! Lets say Bob's address |
| // is changed to Ontario, but Bob is not registered in the UOW. The query |
| // will not return Bob from the database, and Bob can not be added to the |
| // result at conform time, as Bob is not in the cache. Thus even though |
| // Bob should be returned by the conforming query, as its address is now |
| // in Ontario, it won't be unless Bob is registered first. |
| // Bug 2675667 has been entered for this. |
| Employee outsider = (Employee)nonOntarioEmployees.elementAt(0); |
| this.immigrant = (Employee)uow.registerExistingObject(outsider); |
| this.immigrant.getAddress().setProvince("ONT"); |
| |
| // This is for the bug with not using the correct in-memory indiretion policy |
| // to check the UOW cache. |
| // Bug 2667870 |
| //outsider2 = (Employee)nonOntarioEmployees.elementAt(1); |
| //outsider2 = (Employee)uow.registerExistingObject(outsider2); |
| // At this point there should be only 3 addresses registered in the UOW cache. |
| // Two of them once conformed but no longer do. One of them did not but now |
| // does. |
| query = new ReadAllQuery(Employee.class); |
| expression = new ExpressionBuilder(Employee.class).get("address").get("province").equal("ONT"); |
| query.setSelectionCriteria(expression); |
| query.setShouldRegisterResultsInUnitOfWork(false); |
| query.conformResultsInUnitOfWork(); |
| // When bug 2667870 is fixed this line should no longer be needed. |
| query.getInMemoryQueryIndirectionPolicy().triggerIndirection(); |
| } |
| |
| @Override |
| public void test() { |
| this.result = (Vector)this.uow.executeQuery(this.query); |
| |
| // Also test for ReadObjectQuery... |
| ReadObjectQuery emigrantQuery = new ReadObjectQuery(Employee.class); |
| ExpressionBuilder builder = new ExpressionBuilder(Employee.class); |
| Expression expression = builder.get("address").get("province").equal("ONT"); |
| expression = expression.and(builder.get("firstName").equal(this.emigrant.getFirstName())); |
| emigrantQuery.setSelectionCriteria(expression); |
| emigrantQuery.setShouldRegisterResultsInUnitOfWork(false); |
| emigrantQuery.conformResultsInUnitOfWork(); |
| this.result2 = this.uow.executeQuery(emigrantQuery); |
| |
| // Also test for ReadObjectQuery... |
| emigrantQuery = new ReadObjectQuery(Employee.class); |
| builder = new ExpressionBuilder(Employee.class); |
| expression = builder.get("address").get("province").equal("ONT"); |
| expression = expression.and(builder.equal(this.emigrant)); |
| emigrantQuery.setSelectionCriteria(expression); |
| emigrantQuery.setShouldRegisterResultsInUnitOfWork(false); |
| emigrantQuery.conformResultsInUnitOfWork(); |
| this.result3 = this.uow.executeQuery(emigrantQuery); |
| } |
| |
| @Override |
| protected void verify() { |
| // First check that reading through the unit of work properly conformed |
| // to the changes made in the unit of work. |
| if (this.result.contains(this.emigrant)) { |
| throw new TestErrorException("An employee no longer in Ontario was returned."); |
| } |
| if (!this.result.contains(this.immigrant)) { |
| throw new TestErrorException("An employee that now lives in Ontario was not returned."); |
| } |
| if (this.result.size() != (this.numberOfOntarioAddresses)) { |
| throw new TestErrorException("Expecting: " + (this.numberOfOntarioAddresses) + " retrieved: " + this.result.size()); |
| } |
| |
| // Now check that no addresses were registered and put in the unit of work cache. |
| Vector registeredAddresses = uow.getIdentityMapAccessor().getAllFromIdentityMap(null, Address.class, null, null); |
| if (registeredAddresses.size() != 3) { |
| throw new TestErrorException("Should be only three addresses registered in UOW cache, not: " + registeredAddresses.size()); |
| } |
| |
| // Now check that no employees were registered and put in the UOW cache. |
| Vector registeredEmployees = uow.getIdentityMapAccessor().getAllFromIdentityMap(null, Employee.class, null, null); |
| if (registeredEmployees.size() != 2) { |
| throw new TestErrorException("Should be only two employees registered in UOW cache, not: " + registeredEmployees.size()); |
| } |
| |
| // Now verify that no registered objects point to unregistered objects. |
| // Also verify that no unregistered objects point to registered objects. |
| for (Enumeration enumtr = result.elements(); enumtr.hasMoreElements();) { |
| Employee unregistered = (Employee)enumtr.nextElement(); |
| if ((unregistered != this.immigrant) && registeredAddresses.contains(unregistered.getAddress())) { |
| throw new TestErrorException("An unregistered object references a registered object."); |
| } |
| } |
| |
| // Now check that the read object query returned nothing: this should be |
| // automatic if the above tests passed... |
| if (result2 != null) { |
| throw new TestErrorException("A no longer conforming object was returned by a read object query: " + result2 + ", emigrant: " + this.emigrant2); |
| } |
| |
| // Now check that the read object query returned nothing: this should be |
| // automatic if the above tests passed... |
| if (result3 != null) { |
| throw new TestErrorException("A no longer conforming object was returned by a read object query: " + result3 + ", emigrant: " + this.emigrant); |
| } |
| |
| // Should also check that we can now safely register the result using |
| // registerExistingObject. |
| for (Enumeration enumtr = result.elements(); enumtr.hasMoreElements();) { |
| Employee unregistered = (Employee)enumtr.nextElement(); |
| Employee registeredEmp = (Employee)uow.registerExistingObject(unregistered); |
| Vector unregisteredAddresses = getSession().getIdentityMapAccessor().getAllFromIdentityMap(null, Address.class, null, null); |
| if (unregisteredAddresses.contains(registeredEmp.getAddress()) || (!registeredEmp.getAddress().getProvince().equals("ONT") && !registeredEmp.getFirstName().equals(emigrant2.getFirstName()))) { |
| throw new TestErrorException("Unregistered objects could not be properly registered later."); |
| } |
| } |
| } |
| } |