blob: 2a9cd0cc8d1b43fb8916e04a32ee72bdc8304c84 [file] [log] [blame]
/*
* 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.");
}
}
}
}