blob: 317fd05ff159022cdca358604203217adfa289fa [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:
// 05/05/2009 Andrei Ilitchev
// - JPA 2.0 - OrderedList support.
package org.eclipse.persistence.testing.tests.orderedlist;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.eclipse.persistence.annotations.OrderCorrectionType;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.changetracking.AttributeChangeTrackingPolicy;
import org.eclipse.persistence.descriptors.changetracking.ObjectChangePolicy;
import org.eclipse.persistence.descriptors.changetracking.ObjectChangeTrackingPolicy;
import org.eclipse.persistence.exceptions.QueryException;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.indirection.IndirectList;
import org.eclipse.persistence.internal.databaseaccess.DatabasePlatform;
import org.eclipse.persistence.internal.queries.OrderedListContainerPolicy;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.mappings.CollectionMapping;
import org.eclipse.persistence.queries.DataReadQuery;
import org.eclipse.persistence.queries.ReadAllQuery;
import org.eclipse.persistence.queries.ReadObjectQuery;
import org.eclipse.persistence.queries.ReadQuery;
import org.eclipse.persistence.queries.ReportQuery;
import org.eclipse.persistence.queries.ReportQueryResult;
import org.eclipse.persistence.sessions.UnitOfWork;
import org.eclipse.persistence.testing.framework.TestCase;
import org.eclipse.persistence.testing.framework.TestErrorException;
import org.eclipse.persistence.testing.framework.TestModel;
import org.eclipse.persistence.testing.models.orderedlist.*;
import org.eclipse.persistence.testing.models.orderedlist.EmployeeSystem.ChangeTracking;
import org.eclipse.persistence.testing.models.orderedlist.EmployeeSystem.JoinFetchOrBatchRead;
import org.eclipse.persistence.testing.framework.TestProblemException;
public class OrderListTestModel extends TestModel {
/*
* Indicates whether to run configuration that don't use listOrderField (useListOrderField==false).
* By default is set to false.
* Set it to true for debugging:
* if something fails with listOrderField (useListOrderField==true) see if it also fails without it.
*/
static boolean shouldRunWithoutListOrderField = false;
/*
* There is a single top level model (contained in TestRunModel) that contains multiple models.
*/
boolean isTopLevel;
/*
* Top level model loops through all possible combinations of the attributes below and decides whether the combination should run.
* Foe each combination of values that should run a model is created and added to the top level model.
*/
boolean useListOrderField;
boolean isPrivatelyOwned;
boolean useIndirection;
boolean useSecondaryTable;
boolean useVarcharOrder;
ChangeTracking changeTracking;
OrderCorrectionType orderCorrectionType;
boolean shouldOverrideContainerPolicy;
JoinFetchOrBatchRead joinFetchOrBatchRead;
/*
* Variables below used by setup / reset:
* setup saves there the original state of something, sets a new state required for testing,
* then reset brings back the saved original state.
*/
Map<Class, ObjectChangeTrackingPolicy> originalChangeTrackingPolicies;
/*
* Constants used by WhereToAdd tests.
*/
static final String front = "front";
static final String middle = "middle";
static final String end = "end";
/**
* Return the JUnit suite to allow JUnit runner to find it.
* Unfortunately JUnit only allows suite methods to be static,
* so it is not possible to generically do this.
*/
public static junit.framework.TestSuite suite() {
return new OrderListTestModel();
}
public OrderListTestModel() {
setDescription("This model tests ordered list.");
isTopLevel = true;
}
void addTestModel(DatabasePlatform platform) {
if (shouldAddModel(platform)) {
addTest(new OrderListTestModel(useListOrderField, useIndirection, isPrivatelyOwned, useSecondaryTable, useVarcharOrder, changeTracking, orderCorrectionType, shouldOverrideContainerPolicy, joinFetchOrBatchRead));
}
}
/*
* Loops through all possible model configurations and adds those for which shouldAddModel returns true.
*/
void addModels() {
DatabasePlatform platform = getSession().getPlatform();
changeTracking = ChangeTracking.ATTRIBUTE;
orderCorrectionType = OrderCorrectionType.READ_WRITE;
joinFetchOrBatchRead = JoinFetchOrBatchRead.NONE;
useVarcharOrder = false;
useSecondaryTable = false;
isPrivatelyOwned = false;
useIndirection = true;
useListOrderField = true;
shouldOverrideContainerPolicy = false;
useVarcharOrder = true;
addTestModel(platform);
useVarcharOrder = false;
useSecondaryTable = true;
addTestModel(platform);
useSecondaryTable = false;
useIndirection = false;
changeTracking = ChangeTracking.DEFERRED;
addTestModel(platform);
changeTracking = ChangeTracking.ATTRIBUTE;
useIndirection = true;
shouldOverrideContainerPolicy = true;
addTestModel(platform);
shouldOverrideContainerPolicy = false;
for (int i=0; i < ChangeTracking.values().length; i++) {
changeTracking = ChangeTracking.values()[i];
addTestModel(platform);
}
isPrivatelyOwned = true;
for (int i=0; i < ChangeTracking.values().length; i++) {
changeTracking = ChangeTracking.values()[i];
addTestModel(platform);
}
changeTracking = ChangeTracking.ATTRIBUTE;
isPrivatelyOwned = false;
for (int j=0; j < OrderCorrectionType.values().length; j++) {
orderCorrectionType = OrderCorrectionType.values()[j];
addTestModel(platform);
}
orderCorrectionType = OrderCorrectionType.READ_WRITE;
for (int k=0; k < JoinFetchOrBatchRead.values().length; k++) {
joinFetchOrBatchRead = JoinFetchOrBatchRead.values()[k];
addTestModel(platform);
}
joinFetchOrBatchRead = JoinFetchOrBatchRead.NONE;
useSecondaryTable = true;
for (int k=0; k < JoinFetchOrBatchRead.values().length; k++) {
joinFetchOrBatchRead = JoinFetchOrBatchRead.values()[k];
addTestModel(platform);
}
joinFetchOrBatchRead = JoinFetchOrBatchRead.NONE;
useSecondaryTable = false;
/** Un-comment to run 2^6 * 3 * 6 * 15 tests... (a lot...)
do {
do {
do {
do {
do {
for(int i=0; i < ChangeTracking.values().length; i++) {
changeTracking = ChangeTracking.values()[i];
for(int j=0; j < OrderCorrectionType.values().length; j++) {
orderCorrectionType = OrderCorrectionType.values()[j];
do{
for(int k=0; k < JoinFetchOrBatchRead.values().length; k++) {
joinFetchOrBatchRead = JoinFetchOrBatchRead.values()[k];
addTestModel(platform);
}
shouldOverrideContainerPolicy = !shouldOverrideContainerPolicy;
} while(shouldOverrideContainerPolicy);
}
}
useVarcharOrder = !useVarcharOrder;
} while(useVarcharOrder);
useSecondaryTable = !useSecondaryTable;
} while(useSecondaryTable);
isPrivatelyOwned = !isPrivatelyOwned;
} while(isPrivatelyOwned);
useIndirection = !useIndirection;
} while(useIndirection);
useListOrderField = !useListOrderField;
} while(useListOrderField);*/
}
/*
* Verifies whether the current model configuration should be added.
* Cuts the models with invalid configurations, configurations that don't make any difference.
*/
boolean shouldAddModel(DatabasePlatform platform) {
// listOrderField is not used
if(!useListOrderField) {
// explicitly asked not to run the model that don't use listOrderField.
if(!shouldRunWithoutListOrderField) {
return false;
}
// the model would be identical to OrderCorrectionType.READ
if(orderCorrectionType != OrderCorrectionType.READ) {
return false;
}
// the model would be identical to useVarcharOrder==false
if(useVarcharOrder) {
return false;
}
}
// H2 has an issue with large outer joins, causes null-pointer in driver.
//
// There is an Eclipselink bug: when using old Oracle-style (+) outer joins the inner joins between
// primary and secondary tables substituted by outer joins (employee outer join manager causes manager's salary to outer join to manager).
// If there happens to be another outer join to the secondary table (in useSecondaryTable case manager_id is in salary table)
// then suddenly the secondary table auto joined to two tables - that causes exception:
// ORA-01417: a table may be outer joined to at most one other table.
// Now by default Oracle joins in FROM clause, TimesTen can't, therefore TimesTen can't run this model.
//
if (useSecondaryTable && (joinFetchOrBatchRead == JoinFetchOrBatchRead.OUTER_JOIN)) {
if (platform.isH2() || platform.isHSQL() || platform.isTimesTen()) {
return false;
}
}
if (useVarcharOrder && !platform.supportsAutoConversionToNumericForArithmeticOperations()) {
return false;
}
return true;
}
public OrderListTestModel(boolean useListOrderField, boolean useIndirection, boolean isPrivatelyOwned, boolean useSecondaryTable, boolean useVarcharOrder, ChangeTracking changeTracking, OrderCorrectionType orderCorrectionType, boolean shouldOverrideContainerPolicy, JoinFetchOrBatchRead joinFetchOrBatchRead) {
this.useListOrderField = useListOrderField;
this.useIndirection = useIndirection;
this.isPrivatelyOwned = isPrivatelyOwned;
this.useSecondaryTable = useSecondaryTable;
this.useVarcharOrder = useVarcharOrder;
this.changeTracking = changeTracking;
this.orderCorrectionType = orderCorrectionType;
this.shouldOverrideContainerPolicy = shouldOverrideContainerPolicy;
this.joinFetchOrBatchRead = joinFetchOrBatchRead;
setDescription("This model tests ordered list");
setName("");
addToName(useListOrderField ? "" : "NO_ORDER_LIST");
addToName(useIndirection ? "" : "NO_INDIRECTION");
addToName(isPrivatelyOwned ? "PRIVATE" : "");
addToName(useSecondaryTable ? "SECONDARY_TABLE" : "");
addToName(useVarcharOrder ? "VARCHAR_ORDER" : "");
addToName(changeTracking.toString());
addToName(orderCorrectionType == OrderCorrectionType.READ ? "" : orderCorrectionType.toString());
addToName(shouldOverrideContainerPolicy ? "OVERRIDE_CONTAINER_POLICY" : "");
addToName(joinFetchOrBatchRead.toString());
}
void addToName(String strToAdd) {
if(strToAdd.length() > 0) {
setName(getName() + " " + strToAdd);
}
}
@Override
public void addRequiredSystems() {
if(!isTopLevel) {
addRequiredSystem(new EmployeeSystem(useListOrderField, useIndirection, isPrivatelyOwned, useSecondaryTable, useVarcharOrder, changeTracking, orderCorrectionType, shouldOverrideContainerPolicy, joinFetchOrBatchRead));
}
}
@Override
public void addTests() {
if(!isTopLevel) {
addTest(new CreateTest());
addTest(new SimpleAddRemoveTest());
// takes too long with joins
if(joinFetchOrBatchRead != JoinFetchOrBatchRead.INNER_JOIN && joinFetchOrBatchRead != JoinFetchOrBatchRead.OUTER_JOIN) {
addTest(new SimpleAddRemoveTest2());
}
// can't run with inner joins because the test sets useResponsibilities and usePhones to false and these lists are empty.
if(joinFetchOrBatchRead != JoinFetchOrBatchRead.INNER_JOIN) {
addTest(new AddRemoveUpdateTest());
}
addTest(new VerifyForeignKeyOfRemovedObject(false));
addTest(new VerifyForeignKeyOfRemovedObject(true));
addTest(new SimpleSetTest());
addTest(new SimpleSetListTest());
addTest(new SimpleSetListTest(false));
addTest(new SimpleSetListTest(true));
addTest(new TranspositionTest(new int[]{0, 1}, new int[]{1, 0}, false));
addTest(new TranspositionTest(new int[]{0, 1}, new int[]{1, 0}, true));
addTest(new TranspositionMergeTest(new int[]{0, 1}, new int[]{1, 0}));
addTest(new TranspositionTest(new int[]{1, 3, 5}, new int[]{5, 1, 3}, false));
addTest(new TranspositionTest(new int[]{1, 3, 5}, new int[]{5, 1, 3}, true));
addTest(new TranspositionMergeTest(new int[]{1, 3, 5}, new int[]{5, 1, 3}));
// currently only DirectCollectionMapping supports nulls.
// bug 278126: Aggregate and Direct collections containing nulls read incorrectly if join is used.
// When the bug is fixed the tests should work and condition should be removed.
// The bug is partially fixed - the only case result is wrong for DirectCollectionMapping
// is a collection that has a single element - null. That collection is read in as empty.
addTest(new AddNullTest(front));
addTest(new AddNullTest(middle));
addTest(new AddNullTest(end));
// currently only DirectCollectionMapping supports duplicates.
// Duplication doesn't work with joining because of SELECT DISTINCT (would work without DISTINCT).
// Should DISTINCT be there?
if(joinFetchOrBatchRead != JoinFetchOrBatchRead.INNER_JOIN && joinFetchOrBatchRead != JoinFetchOrBatchRead.OUTER_JOIN) {
addTest(new AddDuplicateTest(front));
addTest(new AddDuplicateTest(middle));
addTest(new AddDuplicateTest(end));
}
if(joinFetchOrBatchRead == JoinFetchOrBatchRead.OUTER_JOIN) {
addTest(new CreateEmptyTest());
addTest(new CreateEmptyManagersTest());
}
if(this.useListOrderField) {
addTest(new SimpleIndexTest(true));
if(this.shouldOverrideContainerPolicy) {
addTest(new VerifyContainerPolicyClassTest());
}
if(orderCorrectionType == OrderCorrectionType.EXCEPTION) {
if(joinFetchOrBatchRead != JoinFetchOrBatchRead.INNER_JOIN) {
addTest(new BreakOrderExceptionTest_OneToMany());
}
addTest(new BreakOrderExceptionTest_UnidirectionalOneToMany());
addTest(new BreakOrderExceptionTest_ManyToMany());
addTest(new BreakOrderExceptionTest_DirectCollection());
if(this.changeTracking == ChangeTracking.DEFERRED) {
addTest(new BreakOrderExceptionTest_AggregateCollection());
}
} else if(orderCorrectionType == OrderCorrectionType.READ_WRITE) {
addTest(new BreakOrderCorrectionAndRemoveTest(false));
addTest(new BreakOrderCorrectionAndRemoveTest(true));
addTest(new BreakOrderCorrectionTest(false));
addTest(new BreakOrderCorrectionTest(true));
}
}
addTest(new CreateManagersTest());
} else {
addModels();
}
}
@Override
public void setup() {
if(!isTopLevel) {
if(changeTracking == ChangeTracking.ATTRIBUTE) {
// Save change policies for the all employee demo class in order to restore them at reset time.
Map<Class, ObjectChangePolicy> originalChangeTrackingPolicies = new HashMap<>();
originalChangeTrackingPolicies.put(Employee.class, getSession().getDescriptor(Employee.class).getObjectChangePolicy());
getSession().getDescriptor(Employee.class).setObjectChangePolicy(new AttributeChangeTrackingPolicy());
originalChangeTrackingPolicies.put(Project.class, getSession().getDescriptor(Project.class).getObjectChangePolicy());
getSession().getDescriptor(Project.class).setObjectChangePolicy(new AttributeChangeTrackingPolicy());
originalChangeTrackingPolicies.put(SmallProject.class, getSession().getDescriptor(SmallProject.class).getObjectChangePolicy());
getSession().getDescriptor(SmallProject.class).setObjectChangePolicy(new AttributeChangeTrackingPolicy());
originalChangeTrackingPolicies.put(LargeProject.class, getSession().getDescriptor(LargeProject.class).getObjectChangePolicy());
getSession().getDescriptor(LargeProject.class).setObjectChangePolicy(new AttributeChangeTrackingPolicy());
// currently attribute change tracking is incompatible with AggregateCollectionMapping
if(this.changeTracking != ChangeTracking.ATTRIBUTE) {
originalChangeTrackingPolicies.put(PhoneNumber.class, getSession().getDescriptor(PhoneNumber.class).getObjectChangePolicy());
getSession().getDescriptor(PhoneNumber.class).setObjectChangePolicy(new AttributeChangeTrackingPolicy());
}
}
}
}
@Override
public void reset() {
if(!isTopLevel) {
// restore original change policies.
if(originalChangeTrackingPolicies != null) {
Iterator<Map.Entry<Class, ObjectChangeTrackingPolicy>> it = originalChangeTrackingPolicies.entrySet().iterator();
while(it.hasNext()) {
Map.Entry<Class, ObjectChangeTrackingPolicy> entry = it.next();
getSession().getDescriptor(entry.getKey()).setObjectChangePolicy(entry.getValue());
}
originalChangeTrackingPolicies = null;
}
}
}
/*
* Defines operations on Lists that consist of (in that order):
* Employees (for empManager.managerEmployees);
* Children (for empManager.children);
* Projects (for empManager.projects);
* Strings (for empManager.responsibilitiesList);
* PhoneNumbers (for empManager.phoneNumbers).
* That allows to easily create tests that test all mappings using list order field:
* OneToMany, UnidirectionalOneToMany, ManyToMany, DirectCollection, AggregateCollection.
*
* Note that (for debugging purposes) some of these mappings could be "switched off" by setting the corresponding "use..." flag to false.
* Don't do that in INNER_JOIN case - or no objects will be ever read back from db.
*/
class BaseTest extends TestCase {
boolean useManagedEmployees;
boolean useChildren;
boolean useProjects;
boolean useResponsibilities;
boolean usePhones;
String errorMsg;
BaseTest() {
this.useManagedEmployees = true;
this.useChildren = true;
this.useProjects = true;
this.useResponsibilities = true;
this.usePhones = true;
setValidFlags();
errorMsg = "";
setName(getShortClassName());
}
/*
* Sets some of useManagedEmployees, useChildren, useProjects, useResponsibilities, usePhones to false
* as required by the flags copied from OrderListTestModel.this.
* If changing this method change validateFlags method accordingly.
*/
protected void setValidFlags() {
// currently attribute change tracking is incompatible with AggregateCollectionMapping
if(OrderListTestModel.this.changeTracking == ChangeTracking.ATTRIBUTE) {
usePhones = false;
}
// managedEmployees have nothing: no managedEmployees, no children, no projects, no responsibilities, no phones -
// can't read them using INNER_JOIN
if(OrderListTestModel.this.joinFetchOrBatchRead == JoinFetchOrBatchRead.INNER_JOIN) {
useManagedEmployees = false;
}
}
/*
* Validate useManagedEmployees, useChildren, useProjects, useResponsibilities, usePhones flags
* so their value don't contradict the flags copied from OrderListTestModel.this.
* If changing this method change setValidFlags method accordingly.
*/
protected void validateFlags() {
// currently attribute change tracking is incompatible with AggregateCollectionMapping
if(OrderListTestModel.this.changeTracking == ChangeTracking.ATTRIBUTE) {
if(usePhones) {
errorMsg += "ChangeTracking.ATTRIBUTE requires usePhones==false; ";
}
}
// managedEmployees have nothing: no managedEmployees, no children, no projects, no responsibilities, no phones -
// can't read them using INNER_JOIN
if(OrderListTestModel.this.joinFetchOrBatchRead == JoinFetchOrBatchRead.INNER_JOIN) {
if(useManagedEmployees) {
errorMsg += "JoinFetchOrBatchRead.INNER_JOIN requires useManagedEmployees==false; ";
}
}
if(errorMsg.length() > 0) {
throw new TestProblemException(errorMsg);
}
}
/*
* Debugging: putting a breakpoint at the first line of this method is a good place to set some of
* useManagedEmployees, useChildren, useProjects, useResponsibilities, usePhones to false if desired.
*/
@Override
public void setup() {
if(!useManagedEmployees && !useChildren && !useProjects && !useResponsibilities && !usePhones) {
throw new TestProblemException("useManagedEmployees, useChildren, useProjects, useResponsibilities, usePhones are all false - nothing to test");
}
validateFlags();
}
@Override
public void reset() {
if(useManagedEmployees) {
if(useSecondaryTable) {
getSession().executeNonSelectingSQL("UPDATE OL_SALARY SET MANAGER_ID = NULL");
} else {
getSession().executeNonSelectingSQL("UPDATE OL_EMPLOYEE SET MANAGER_ID = NULL");
}
}
if(useChildren) {
getSession().executeNonSelectingSQL("DELETE FROM OL_ALLOWANCE");
getSession().executeNonSelectingSQL("DELETE FROM OL_CHILD");
}
if(useProjects) {
getSession().executeNonSelectingSQL("DELETE FROM OL_PROJ_EMP");
getSession().executeNonSelectingSQL("DELETE FROM OL_LPROJECT");
getSession().executeNonSelectingSQL("DELETE FROM OL_PROJECT");
}
if(useResponsibilities) {
getSession().executeNonSelectingSQL("DELETE FROM OL_RESPONS");
}
if(usePhones) {
getSession().executeNonSelectingSQL("DELETE FROM OL_PHONE");
}
getSession().executeNonSelectingSQL("DELETE FROM OL_SALARY");
getSession().executeNonSelectingSQL("DELETE FROM OL_EMPLOYEE");
getSession().getIdentityMapAccessor().initializeAllIdentityMaps();
errorMsg = "";
}
/*
* Creates a list of objects that could be added to manager: Employee, Child, Project, Responsibility (String), PhoneNumber.
* Note that indexForName here is used as part of a state of the object being created - not as its position in any list.
*/
List create(String prefix, int indexForName) {
List list = new ArrayList();
String iString = Integer.toString(indexForName);
String str = prefix + iString;
if(useManagedEmployees) {
list.add(new Employee(iString, prefix));
}
if(useChildren) {
list.add(new Child(iString, prefix));
}
if(useProjects) {
if(indexForName % 2 == 0) {
list.add(new SmallProject(str));
} else {
list.add(new LargeProject(str));
}
}
if(useResponsibilities) {
list.add(str);
}
if(usePhones) {
if(prefix.length() > 3) {
prefix = prefix.substring(0, 3);
}
list.add(new PhoneNumber(prefix, iString));
}
return list;
}
/*
* Creates a list of nulls that could be added to manager: Employee, Child, Project, Responsibility (String), PhoneNumber.
*/
List createNull() {
List list = new ArrayList();
if(useManagedEmployees) {
list.add(null);
}
if(useChildren) {
list.add(null);
}
if(useProjects) {
list.add(null);
}
if(useResponsibilities) {
list.add(null);
}
if(usePhones) {
list.add(null);
}
return list;
}
/*
* Adds a list of objects to empManager: Employee, Child, Project, Responsibility (String), PhoneNumber.
* The list should be obtained from getFrom, create, removeFrom or setInto methods.
*/
void addTo(Employee empManager, List list) {
int i = 0;
if(useManagedEmployees) {
empManager.addManagedEmployee((Employee)list.get(i++));
}
if(useChildren) {
empManager.getChildren().add(list.get(i++));
}
if(useProjects) {
empManager.addProject((Project)list.get(i++));
}
if(useResponsibilities) {
empManager.addResponsibility((String)list.get(i++));
}
if(usePhones) {
empManager.addPhoneNumber((PhoneNumber)list.get(i++));
}
}
/*
* Adds a list of objects to empManager at index: Employee, Child, Project, Responsibility (String), PhoneNumber.
* The list should be obtained from getFrom, create, removeFrom or setInto methods.
*/
void addTo(Employee empManager, int index, List list) {
int i = 0;
if(useManagedEmployees) {
empManager.addManagedEmployee(index, (Employee)list.get(i++));
}
if(useChildren) {
empManager.getChildren().add(index, list.get(i++));
}
if(useProjects) {
empManager.addProject(index, (Project)list.get(i++));
}
if(useResponsibilities) {
empManager.addResponsibility(index, (String)list.get(i++));
}
if(usePhones) {
empManager.addPhoneNumber(index, (PhoneNumber)list.get(i++));
}
}
/*
* Adds a list of objects to empManager at index: Employee, Child, Project, Responsibility (String), PhoneNumber.
* The list should be obtained from getFrom, create, removeFrom or setInto methods.
*/
void addTo_NoRelMaintanence(Employee empManager, int index, List list) {
int i = 0;
if(useManagedEmployees) {
empManager.getManagedEmployees().add(index, (Employee)list.get(i++));
}
if(useChildren) {
empManager.getChildren().add(index, list.get(i++));
}
if(useProjects) {
empManager.getProjects().add(index, (Project)list.get(i++));
}
if(useResponsibilities) {
empManager.addResponsibility(index, (String)list.get(i++));
}
if(usePhones) {
empManager.addPhoneNumber(index, (PhoneNumber)list.get(i++));
}
}
/*
* Removes a list of objects from empManager at index.
* Returns list of removed objects: Employee, Child, Project, Responsibility (String), PhoneNumber.
*/
List removeFrom(Employee empManager, int index) {
List list = new ArrayList();
if(useManagedEmployees) {
list.add(empManager.removeManagedEmployee(index));
}
if(useChildren) {
list.add(empManager.getChildren().remove(index));
}
if(useProjects) {
list.add(empManager.removeProject(index));
}
if(useResponsibilities) {
list.add(empManager.removeResponsibility(index));
}
if(usePhones) {
list.add(empManager.removePhoneNumber(index));
}
return list;
}
/*
* Removes a list of objects from empManager at index.
* Returns list of removed objects: Employee, Child, Project, Responsibility (String), PhoneNumber.
*/
List removeFrom_NoRelMaintanence(Employee empManager, int index) {
List list = new ArrayList();
if(useManagedEmployees) {
list.add(empManager.getManagedEmployees().remove(index));
}
if(useChildren) {
list.add(empManager.getChildren().remove(index));
}
if(useProjects) {
list.add(empManager.getProjects().remove(index));
}
if(useResponsibilities) {
list.add(empManager.removeResponsibility(index));
}
if(usePhones) {
list.add(empManager.removePhoneNumber(index));
}
return list;
}
/*
* Gets a list of objects from empManager at index: Employee, Child, Project, Responsibility (String), PhoneNumber.
*/
List getFrom(Employee empManager, int index) {
List list = new ArrayList();
if(useManagedEmployees) {
list.add(empManager.getManagedEmployees().get(index));
}
if(useChildren) {
list.add(empManager.getChildren().get(index));
}
if(useProjects) {
list.add(empManager.getProjects().get(index));
}
if(useResponsibilities) {
list.add(empManager.getResponsibilitiesList().get(index));
}
if(usePhones) {
list.add(empManager.getPhoneNumbers().get(index));
}
return list;
}
/*
* Sets a list of objects into empManager at index.
* Returns list of removed objects: Employee, Child, Project, Responsibility (String), PhoneNumber.
*/
List setInto(Employee empManager, int index, List list) {
int i = 0;
List listOut = new ArrayList();
if(useManagedEmployees) {
listOut.add(empManager.setManagedEmployee(index, (Employee)list.get(i++)));
}
if(useChildren) {
listOut.add(empManager.getChildren().set(index, list.get(i++)));
}
if(useProjects) {
listOut.add(empManager.setProject(index, (Project)list.get(i++)));
}
if(useResponsibilities) {
listOut.add(empManager.setResponsibility(index, (String)list.get(i++)));
}
if(usePhones) {
listOut.add(empManager.setPhoneNumber(index, (PhoneNumber)list.get(i++)));
}
return listOut;
}
/*
* Sets a list of objects into empManager at index.
* Returns list of removed objects: Employee, Child, Project, Responsibility (String), PhoneNumber.
*/
List setInto_NoRelMaintanence(Employee empManager, int index, List list) {
int i = 0;
List listOut = new ArrayList();
if(useManagedEmployees) {
listOut.add(empManager.getManagedEmployees().set(index, (Employee)list.get(i++)));
}
if(useChildren) {
listOut.add(empManager.getChildren().set(index, list.get(i++)));
}
if(useProjects) {
listOut.add(empManager.getProjects().set(index, (Project)list.get(i++)));
}
if(useResponsibilities) {
listOut.add(empManager.setResponsibility(index, (String)list.get(i++)));
}
if(usePhones) {
listOut.add(empManager.setPhoneNumber(index, (PhoneNumber)list.get(i++)));
}
return listOut;
}
/*
* Sets lists of objects into empManager using setManagedEmployees, setChildren, setProjects, setResponsibilityList, setPhoneNumbers methods.
* The list should be created with createList method.
*/
void setListInto(Employee empManager, List<List> listOfLists) {
int i = 0;
if(useManagedEmployees) {
List<Employee> newList = listOfLists.get(i++);
List<Employee> oldList = empManager.getManagedEmployees();
empManager.setManagedEmployees(newList);
if(oldList != null) {
for(int j=0; j < oldList.size(); j++) {
oldList.get(j).setManager(null);
}
}
if(newList != null) {
for(int j=0; j < newList.size(); j++) {
newList.get(j).setManager(empManager);
}
}
}
if(useChildren) {
empManager.setChildren((Vector)listOfLists.get(i++));
}
if(useProjects) {
List<Project> newList = listOfLists.get(i++);
List<Project> oldList = empManager.getProjects();
empManager.setProjects(newList);
if(oldList != null) {
for(int j=0; j < oldList.size(); j++) {
oldList.get(j).getEmployees().remove(empManager);
}
}
if(newList != null) {
for(int j=0; j < newList.size(); j++) {
newList.get(j).getEmployees().add(empManager);
}
}
}
if(useResponsibilities) {
empManager.setResponsibilitiesList(listOfLists.get(i++));
}
if(usePhones) {
empManager.setPhoneNumbers(listOfLists.get(i++));
}
}
/*
* Updates a list of objects that could be added to manager: Employee, Child, Project, Responsibility (String), PhoneNumber.
* Note that indexForName here is used as part of a state of the object being updated - not as its position in any list.
*/
void update( List list, String prefix, int indexForName) {
String iString = Integer.toString(indexForName);
int i = 0;
if(useManagedEmployees) {
Employee emp = (Employee)list.get(i++);
emp.setFirstName(iString);
emp.setLastName(prefix);
}
if(useChildren) {
Child child = (Child)list.get(i++);
child.setFirstName(iString);
child.setLastName(prefix);
}
if(useProjects) {
Project project = (Project)list.get(i++);
project.setName(iString);
}
if(useResponsibilities) {
throw new TestProblemException("Can't update a String. Set useResponsibilities to false");
}
if(usePhones) {
if(prefix.length() > 3) {
prefix = prefix.substring(0, 3);
}
PhoneNumber phone = (PhoneNumber)list.get(i++);
phone.setAreaCode(prefix);
phone.setNumber(iString);
}
}
/*
* Registers in uow a list of mapped non-aggregated objects that could be added to manager: Employee, Child, Project.
* Returns a list that contains clones that could be updated.
*/
List register(List list, UnitOfWork uow) {
ArrayList listClone = new ArrayList(list.size());
int i = 0;
if(useManagedEmployees) {
listClone.add(uow.registerObject(list.get(i++)));
}
if(useChildren) {
listClone.add(uow.registerObject(list.get(i++)));
}
if(useProjects) {
listClone.add(uow.registerObject(list.get(i++)));
}
if(useResponsibilities) {
throw new TestProblemException("Can't register a String in uow. Set useResponsibilities to false");
}
if(usePhones) {
throw new TestProblemException("Can't register aggregate in uow. Set usePhones to false");
}
return listClone;
}
/*
* Creates a list of Lists that could be set to manager: List<Employee>, Vector, List<Project>, List<String>, List<PhoneNumber>.
*/
List<List> createList() {
List<List> listOfLists = new ArrayList();
if(useManagedEmployees) {
listOfLists.add(new ArrayList<Employee>());
}
if(useChildren) {
listOfLists.add(new Vector());
}
if(useProjects) {
listOfLists.add(new ArrayList<Project>());
}
if(useResponsibilities) {
listOfLists.add(new ArrayList<String>());
}
if(usePhones) {
listOfLists.add(new ArrayList<PhoneNumber>());
}
return listOfLists;
}
/*
* Adds a list of objects to listOfLists: Employee, Child, Project, Responsibility (String), PhoneNumber.
* listOfLists should be created with createList method.
* list should be obtained from getFrom, create, removeFrom or setInto methods.
*/
void addTo(List<List> listOfLists, List list) {
int i = 0;
if(useManagedEmployees) {
listOfLists.get(i).add(list.get(i++));
}
if(useChildren) {
listOfLists.get(i).add(list.get(i++));
}
if(useProjects) {
listOfLists.get(i).add(list.get(i++));
}
if(useResponsibilities) {
listOfLists.get(i).add(list.get(i++));
}
if(usePhones) {
listOfLists.get(i).add(list.get(i++));
}
}
/*
* Breaks order in the db by assigning wrong values to order fields
*/
String getManagegedEmployeesOrderTable() {
return useSecondaryTable ? "OL_SALARY" : "OL_EMPLOYEE";
}
String getManagegedEmployeesOrderField() {
return useVarcharOrder ? "MANAGED_ORDER_VARCHAR" : "MANAGED_ORDER";
}
void breakManagedEmployeesOrder() {
executeBreak(getManagegedEmployeesOrderTable(), getManagegedEmployeesOrderField(), "1", "NULL");
}
String getChildrenOrderTable() {
return useSecondaryTable ? "OL_ALLOWANCE" : "OL_CHILD";
}
String getChildrenOrderField() {
return useVarcharOrder ? "CHILDREN_ORDER_VARCHAR" : "CHILDREN_ORDER";
}
void breakChildrenOrder() {
executeBreak(getChildrenOrderTable(), getChildrenOrderField(), "0", "NULL");
}
void breakProjectsOrder() {
String tableName = "OL_PROJ_EMP";
String fieldName;
if(useVarcharOrder) {
fieldName = "PROJ_ORDER_VARCHAR";
} else {
fieldName = "PROJ_ORDER";
}
executeBreak(tableName, fieldName, "0", "5");
}
void breakResponsibilitiesOrder() {
String tableName = "OL_RESPONS";
String fieldName;
if(useVarcharOrder) {
fieldName = "RESPONS_ORDER_VARCHAR";
} else {
fieldName = "RESPONS_ORDER";
}
executeBreak(tableName, fieldName, "1", "0");
}
void breakPhonesOrder() {
String tableName = "OL_PHONE";
String fieldName;
if(useVarcharOrder) {
fieldName = "PHONE_ORDER_VARCHAR";
} else {
fieldName = "PHONE_ORDER";
}
executeBreak(tableName, fieldName, "0", "1");
}
void executeBreak(String tableName, String fieldName, String oldValue, String newValue) {
getSession().executeNonSelectingSQL("UPDATE "+tableName+" SET "+fieldName+" = "+newValue+" WHERE "+fieldName+" = " + oldValue);
}
void breakOrder() {
if(useManagedEmployees) {
breakManagedEmployeesOrder();
}
if(useChildren) {
breakChildrenOrder();
}
if(useProjects) {
breakProjectsOrder();
}
if(useResponsibilities) {
breakResponsibilitiesOrder();
}
if(usePhones) {
breakPhonesOrder();
}
}
/*
* Set the new OrderCorrectionType, return the old one.
* Verify that the old modes are the same for all mappings.
*/
OrderCorrectionType changeOrderCorrectionType(OrderCorrectionType mode) {
OrderCorrectionType oldMode = null;
if(useManagedEmployees) {
oldMode = changeOrderCorrectionType("managedEmployees", mode, oldMode);
}
if(useChildren) {
oldMode = changeOrderCorrectionType("children", mode, oldMode);
}
if(useProjects) {
oldMode = changeOrderCorrectionType("projects", mode, oldMode);
}
if(useResponsibilities) {
oldMode = changeOrderCorrectionType("responsibilitiesList", mode, oldMode);
}
if(usePhones) {
oldMode = changeOrderCorrectionType("phoneNumbers", mode, oldMode);
}
return oldMode;
}
OrderCorrectionType changeOrderCorrectionType(String attribute, OrderCorrectionType mode, OrderCorrectionType oldMode) {
ClassDescriptor desc = getSession().getDescriptor(Employee.class);
CollectionMapping mapping = (CollectionMapping)desc.getMappingForAttributeName(attribute);
OrderCorrectionType currOldMode = changeOrderCorrectionType(mapping, mode);
if(oldMode != null) {
if(oldMode != currOldMode) {
throw new TestProblemException("OrderCorrectionTypes for " + attribute+ " is " + currOldMode +"; for previous mapping(s) it was " + oldMode);
}
}
return currOldMode;
}
OrderCorrectionType changeOrderCorrectionType(CollectionMapping mapping, OrderCorrectionType mode) {
OrderedListContainerPolicy policy = (OrderedListContainerPolicy)mapping.getContainerPolicy();
OrderCorrectionType oldMode = policy.getOrderCorrectionType();
policy.setOrderCorrectionType(mode);
OrderedListContainerPolicy queryPolicy = null;
if(mapping.getSelectionQuery().isReadAllQuery()) {
queryPolicy = (OrderedListContainerPolicy)((ReadAllQuery)mapping.getSelectionQuery()).getContainerPolicy();
} else if(mapping.getSelectionQuery().isDataReadQuery()) {
queryPolicy = (OrderedListContainerPolicy)((DataReadQuery)mapping.getSelectionQuery()).getContainerPolicy();
}
if(policy != queryPolicy) {
OrderCorrectionType oldModeQuery = queryPolicy.getOrderCorrectionType();
if(oldMode != oldModeQuery) {
throw new TestErrorException(mapping.getAttributeName() + ": OrderCorrectionTypes in container policy is " + oldMode +"; is query is " + oldModeQuery);
}
queryPolicy.setOrderCorrectionType(mode);
}
return oldMode;
}
/*
* Verify IndirectList.isListOrderBrokenInDb flag value.
* Throw exception if it's not expected one.
*/
String verifyIsListOrderBrokenInDb(Employee empManager, boolean expected) {
String localErrorMsg = "";
if(useManagedEmployees) {
localErrorMsg += verifyIsListOrderBrokenInDb(empManager, expected, "managedEmployees");
}
if(useChildren) {
localErrorMsg += verifyIsListOrderBrokenInDb(empManager, expected, "children");
}
if(useProjects) {
localErrorMsg += verifyIsListOrderBrokenInDb(empManager, expected, "projects");
}
if(useResponsibilities) {
localErrorMsg += verifyIsListOrderBrokenInDb(empManager, expected, "responsibilitiesList");
}
if(usePhones) {
localErrorMsg += verifyIsListOrderBrokenInDb(empManager, expected, "phoneNumbers");
}
if(localErrorMsg.length() > 0) {
localErrorMsg = "isListOrderBrokenInDb expected to be " + expected + ". For the following attributes it is " + !expected +": " + localErrorMsg;
}
return localErrorMsg;
}
String verifyIsListOrderBrokenInDb(Employee empManager, boolean expected, String attribute) {
String localErrorMsg = "";
ClassDescriptor desc = getSession().getDescriptor(Employee.class);
CollectionMapping mapping = (CollectionMapping)desc.getMappingForAttributeName(attribute);
Object attributeValue = mapping.getRealAttributeValueFromObject(empManager, getAbstractSession());
if(((IndirectList)attributeValue).isListOrderBrokenInDb() != expected) {
localErrorMsg = attribute + "; ";
}
return localErrorMsg;
}
/*
* Indicates whether all lists were read in
*/
boolean isInstantiated(Employee empManager) {
List list;
String instantiatedStr = "";
String notInstantiatedStr = "";
String str;
boolean isInstantiated;
if(useManagedEmployees) {
list = empManager.getManagedEmployees();
isInstantiated = isInstantiated(list);
str = (isInstantiated ? instantiatedStr : notInstantiatedStr);
str += "managedEmployees; ";
}
if(useChildren) {
list = empManager.getChildren();
isInstantiated = isInstantiated(list);
str = (isInstantiated ? instantiatedStr : notInstantiatedStr);
str += "children; ";
}
if(useProjects) {
list = empManager.getProjects();
isInstantiated = isInstantiated(list);
str = (isInstantiated ? instantiatedStr : notInstantiatedStr);
str += "projects; ";
}
if(useResponsibilities) {
list = empManager.getResponsibilitiesList();
isInstantiated = isInstantiated(list);
str = (isInstantiated ? instantiatedStr : notInstantiatedStr);
str += "responsibilities; ";
}
if(usePhones) {
list = empManager.getPhoneNumbers();
isInstantiated = isInstantiated(list);
str = (isInstantiated ? instantiatedStr : notInstantiatedStr);
str += "phoneNumbers; ";
}
if(instantiatedStr.length() > 0 && notInstantiatedStr.length() > 0) {
throw new TestProblemException("Some attributes are instantiated: " + instantiatedStr + " and some are not: " + notInstantiatedStr);
} else {
return instantiatedStr.length() > 0;
}
}
boolean isInstantiated(List list) {
return !(list instanceof IndirectList && !((IndirectList)list).isInstantiated());
}
String getShortClassName() {
String name = this.getClass().getName();
int begin = name.indexOf('$') + 1;
int end = name.length();
return name.substring(begin, end);
}
}
/*
* Base class for tests using Employee manager object.
* createManager method creates a manager with nSize of managedEmployees, children, projects, responsibilities, phones.
*/
class BaseManagerTest extends BaseTest {
/*
* Number of manager's managedEmployees, children, projects, responsibilities, phones
* created by createManager method.
*/
int nSize;
Employee manager;
/*
* manager's clone in uow.
*/
Employee managerClone;
BaseManagerTest() {
super();
this.nSize = 2;
}
/*
* Creates manager with nSize members
*/
void createManager() {
manager = new Employee("Manager");
for(int i=0; i < nSize; i++) {
this.addTo(manager, create("old", i));
}
UnitOfWork uow = getSession().acquireUnitOfWork();
managerClone = (Employee)uow.registerObject(manager);
uow.commit();
}
@Override
protected void verify() {
if(manager == null) {
throw new TestErrorException("manager == null. Nothing to verify");
}
String textNameExt;
for(int k=0; k<2; k++) {
if(k == 0) {
textNameExt = "Cache";
} else {
textNameExt = "DB";
// Read back the inserted objects, make sure the order is correct.
getSession().getIdentityMapAccessor().initializeAllIdentityMaps();
manager = (Employee)getSession().readObject(manager);
}
if(!getAbstractSession().compareObjects(managerClone, manager)) {
errorMsg += textNameExt + ": " + "objects not equal\n";
}
}
if(errorMsg.length() > 0) {
throw new TestErrorException('\n' + errorMsg);
}
}
// assumes that listToVerify is in cache and contains mapped object only (can't contain, say String).
protected void verifyList(List listToVerify, List listToCompareTo) {
if(listToVerify == null) {
throw new TestErrorException("listToVerify is null. Nothing to verify");
}
int size = listToVerify.size();
String textNameExt;
for(int k=0; k<2; k++) {
if(k == 0) {
textNameExt = "Cache";
} else {
textNameExt = "DB";
// Read back the objects from db
getSession().getIdentityMapAccessor().initializeAllIdentityMaps();
for(int i=0; i < size; i++) {
listToVerify.set(i, getSession().readObject(listToVerify.get(i)));
}
}
for(int i=0; i < size; i++) {
Object toVerify = listToVerify.get(i);
Object toCompareTo = listToCompareTo.get(i);
if(!getAbstractSession().compareObjects(toCompareTo, toVerify)) {
errorMsg += textNameExt + ": " + "objects not equal\n";
}
}
}
}
// verifies that listToVerify is not in cache and not in db. listToVerify contains mapped object only (can't contain, say String).
protected void verifyListRemoved(List listToVerify) {
if(listToVerify == null) {
throw new TestErrorException("listToVerify is null. Nothing to verify");
}
int size = listToVerify.size();
String textNameExt;
for(int k=0; k<2; k++) {
if(k == 0) {
textNameExt = "Cache";
for(int i=0; i < size; i++) {
ReadObjectQuery query = new ReadObjectQuery();
query.setSelectionObject(listToVerify.get(i));
query.checkCacheOnly();
Object readObject = getSession().executeQuery(query);
if(readObject != null) {
errorMsg += textNameExt + ": " + readObject + " was not removed\n";
}
}
} else {
textNameExt = "DB";
// Read back the objects from db
getSession().getIdentityMapAccessor().initializeAllIdentityMaps();
for(int i=0; i < size; i++) {
Object readObject = getSession().readObject(listToVerify.get(i));
if(readObject != null) {
errorMsg += textNameExt + ": " + readObject + " was not removed\n";
}
}
}
}
}
@Override
public void reset() {
super.reset();
manager = null;
managerClone = null;
}
String nSizeName() {
return getShortClassName() + ": "+nSize+":";
}
}
/*
* Create manager, save to the db, verify that it was correctly merged into shared cache and written into the db.
*/
class CreateTest extends BaseManagerTest {
CreateTest() {
super();
}
@Override
public void test() {
createManager();
}
}
/*
* Create empty manager, save to the db, verify that it was correctly merged into shared cache and written into the db.
* For OUTER_JOIN case to verify that read back lists are empty (as opposed to having null members).
*/
class CreateEmptyTest extends CreateTest {
CreateEmptyTest() {
super();
nSize = 0;
}
}
/*
* Create manager, save to the db. Meant as a base to the tests that change manager.
*/
class ChangeTest extends BaseManagerTest {
ChangeTest() {
super();
}
@Override
public void setup() {
super.setup();
createManager();
}
}
/*
* Switch positions of list members.
* [0, 2], [7, 5] means that old[0] = new[7], old[2] = new[5];
* [0, 2, 5], [7, 5, 4] means that old[0] = new[7], old[2] = new[5], old[5] = new[4].
* useSet indicates whether to use set(index, obj) method or remove(index)/add(index, obj).
*/
class TranspositionTest extends ChangeTest {
int[] oldIndexes, newIndexes;
boolean useSet;
/*
* nSize is number of members in each of the lists (managedEmployees, children etc), it should be greater than any old or new index
*/
TranspositionTest(int nSize, int[] oldIndexes, int[] newIndexes, boolean useSet) {
super();
this.nSize = nSize;
this.oldIndexes = oldIndexes;
this.newIndexes = newIndexes;
this.useSet = useSet;
verifyIndexes();
for(int i=0; i<oldIndexes.length; i++) {
if(oldIndexes[i] >= nSize) {
throw new TestProblemException("oldIndex["+i+"] = "+oldIndexes[i]+", which is greater than nSize ="+nSize);
}
if(newIndexes[i] >= nSize) {
throw new TestProblemException("newIndex["+i+"] = "+newIndexes[i]+", which is greater than nSize ="+nSize);
}
}
setName();
}
/*
* nSize is number of members in each of the lists (managedEmployees, children etc),
* it will be set to the max index + 1.
*/
TranspositionTest(int[] oldIndexes, int[] newIndexes, boolean useSet) {
super();
this.oldIndexes = oldIndexes;
this.newIndexes = newIndexes;
this.useSet = useSet;
verifyIndexes();
nSize = 0;
for(int i=0; i<oldIndexes.length; i++) {
if(oldIndexes[i] > nSize) {
nSize = oldIndexes[i];
}
if(newIndexes[i] > nSize) {
nSize = newIndexes[i];
}
}
nSize++;
setName();
}
public void verifyIndexes() {
if(oldIndexes.length != newIndexes.length) {
throw new TestProblemException("oldIndexes.length != newIndexes.length");
}
for(int i=0; i < oldIndexes.length; i++) {
int oldIndex = oldIndexes[i];
boolean found = false;
for(int j=0; j < newIndexes.length; j++) {
if(oldIndex == newIndexes[j]) {
found = true;
break;
}
}
if(!found) {
throw new TestProblemException("oldIndexes["+i+"] value "+oldIndex+" not found in newIndexes");
}
}
}
String toString(int[] array) {
String str = "[";
for(int i=0; i<array.length; i++) {
str += Integer.toString(array[i]);
if(i < array.length-1) {
str += ", ";
} else {
str += "]";
}
}
return str;
}
void setName() {
setName(getName() + " " + toString(oldIndexes) + " -> " + toString(newIndexes) + (useSet ? " set" : " remove/add"));
}
public void transpose(UnitOfWork uow) {
managerClone = (Employee)uow.registerObject(manager);
int n = oldIndexes.length;
List[] lists = new ArrayList[n];
for(int i=0; i<n; i++) {
lists[i] = getFrom(managerClone, oldIndexes[i]);
}
for(int i=0; i<n; i++) {
int newIndex = newIndexes[i];
if(useSet) {
setInto_NoRelMaintanence(managerClone, newIndex, lists[i]);
} else {
removeFrom_NoRelMaintanence(managerClone, newIndex);
addTo_NoRelMaintanence(managerClone, newIndex, lists[i]);
}
}
}
@Override
public void test() {
UnitOfWork uow = getSession().acquireUnitOfWork();
transpose(uow);
uow.commit();
}
}
/*
* Same as TranspositionTest, but the owner of transposed lists is detached, then merged.
*/
class TranspositionMergeTest extends TranspositionTest {
// This test transposes objects in detached collection, doesn't care whether set or remove/add
TranspositionMergeTest(int nSize, int[] oldIndexes, int[] newIndexes) {
super(nSize, oldIndexes, newIndexes, true);
}
TranspositionMergeTest(int[] oldIndexes, int[] newIndexes) {
// This test transposes objects in detached collection, doesn't care whether set or remove/add
super(oldIndexes, newIndexes, true);
}
@Override
void setName() {
setName(getName() + " " + toString(oldIndexes) + " -> " + toString(newIndexes));
}
@Override
public void test() {
UnitOfWork uow = getSession().acquireUnitOfWork();
transpose(uow);
uow.unregisterObject(this.managerClone);
uow.release();
uow = getSession().acquireUnitOfWork();
this.managerClone = (Employee)uow.mergeCloneWithReferences(this.managerClone);
uow.commit();
}
}
/*
* For each mapping: add one new object, remove one existing object
*/
class SimpleAddRemoveTest extends ChangeTest {
SimpleAddRemoveTest() {
super();
}
@Override
public void test() {
UnitOfWork uow = getSession().acquireUnitOfWork();
managerClone = (Employee)uow.registerObject(manager);
addTo(managerClone, create("new", 1));
removeFrom(managerClone, 0);
uow.commit();
}
}
/*
* For each mapping: add one new object, remove one existing object
*/
class AddRemoveUpdateTest extends ChangeTest {
List removedList;
List removedListClone;
AddRemoveUpdateTest() {
super();
}
@Override
public void setup() {
// Strings are immutable - can't update.
useResponsibilities = false;
// Can't directly register an aggregate in uow.
usePhones = false;
super.setup();
}
@Override
public void test() {
// create a list of objects to be added
List newList = this.create("new", 0);
UnitOfWork uow = getSession().acquireUnitOfWork();
List newListClone = this.register(newList, uow);
uow.commit();
// save list to be removed
removedList = this.getFrom(manager, 0);
uow = getSession().acquireUnitOfWork();
managerClone = (Employee)uow.registerObject(manager);
// update new list clone
newListClone = this.register(newList, uow);
update(newListClone, "updated", 0);
// add updated objects to managerClone
addTo(managerClone, newListClone);
// remove from managerClone
removedListClone = removeFrom(managerClone, 0);
// update removed objects
update(removedListClone, "updated", 0);
uow.commit();
}
@Override
public void verify() {
super.verify();
if(isPrivatelyOwned) {
verifyListRemoved(removedList);
} else {
verifyList(removedList, removedListClone);
}
if(errorMsg.length() > 0) {
throw new TestErrorException('\n' + errorMsg);
}
}
}
/*
* For each mapping: add one new object, remove one existing object
*/
class SimpleAddRemoveTest2 extends ChangeTest {
SimpleAddRemoveTest2() {
super();
nSize = 12;
}
@Override
public void test() {
UnitOfWork uow = getSession().acquireUnitOfWork();
managerClone = (Employee)uow.registerObject(manager);
addTo(managerClone, 0, create("new", 0));
addTo(managerClone, 4, create("new", 4));
removeFrom(managerClone, 8);
uow.commit();
}
}
/*
* For each mapping: set a new object
*/
class SimpleSetTest extends ChangeTest {
SimpleSetTest() {
super();
}
@Override
public void test() {
UnitOfWork uow = getSession().acquireUnitOfWork();
managerClone = (Employee)uow.registerObject(manager);
setInto(managerClone, 1, create("new", 1));
uow.commit();
}
}
/*
* For each mapping: create a new List of two objects, set the new List into managerClone;
* if useSet is not null, before setting a new list either add or set a new object into the old list.
*/
class SimpleSetListTest extends ChangeTest {
Boolean useSet;
SimpleSetListTest() {
super();
}
SimpleSetListTest(boolean useSet) {
super();
this.useSet = useSet;
setName(getName() + (useSet ? " set" : " remove/add"));
}
@Override
public void test() {
UnitOfWork uow = getSession().acquireUnitOfWork();
managerClone = (Employee)uow.registerObject(manager);
if(useSet != null) {
if(useSet) {
setInto(managerClone, 1, create("temp", 1));
} else {
addTo(managerClone, create("temp", 2));
}
}
List<List> list = createList();
addTo(list, create("new", 0));
addTo(list, create("new", 1));
setListInto(managerClone, list);
uow.commit();
}
}
class SimpleIndexTest extends ChangeTest {
int min;
int max;
int nExpected;
boolean useIndex;
SimpleIndexTest(boolean useIndex) {
super();
nSize = 12;
min = 1;
max = 4;
nExpected = max - min + 1;
this.useIndex = useIndex;
setName(getName() + (useIndex ? " use index()" : " use getField()"));
}
@Override
public void test() {
getSession().getIdentityMapAccessor().initializeAllIdentityMaps();
if(useManagedEmployees) {
test("managedEmployees");
}
if(useChildren) {
test("children");
}
if(useProjects) {
test("projects");
}
if(useResponsibilities) {
test("responsibilitiesList");
}
if(usePhones) {
test("phoneNumbers");
}
}
void test(String attributeName) {
ReportQuery query = new ReportQuery();
query.setReferenceClass(Employee.class);
ExpressionBuilder builder = query.getExpressionBuilder();
Expression exp;
Expression firstNameManager = builder.get("firstName").equal("Manager");
Expression anyOfAttribute = builder.anyOf(attributeName);
if(useIndex) {
// tests index() expression
exp = firstNameManager.and(anyOfAttribute.index().between(min, max));
} else {
// tests getField() expression (or getTable().getField() expression) equivalent to index().
if(isTableExpressionRequired(attributeName)) {
exp = firstNameManager.and(anyOfAttribute.getTable(getTableName(attributeName)).getField(getFieldName(attributeName)).between(min, max));
} else {
exp = firstNameManager.and(anyOfAttribute.getField(getFieldName(attributeName)).between(min, max));
}
}
query.setSelectionCriteria(exp);
query.addAttribute(attributeName, anyOfAttribute);
ArrayList indexesRead = new ArrayList(nExpected);
boolean error = false;
List<ReportQueryResult> results = (List)getSession().executeQuery(query);
for(int i=0; i < results.size(); i++) {
ReportQueryResult result = results.get(i);
int index = getIndex(result.getResults().get(0), attributeName);
error |= max < index || index < min;
indexesRead.add(index);
}
String localErrorMsg = "";
if(error) {
localErrorMsg += "Wrong index values read: " + indexesRead +"; expected all numbers between (inclusive) " + min + " and " + max;
}
// query with joins return Cartesian product of all rows - the same values returned more than once.
if(OrderListTestModel.this.joinFetchOrBatchRead != JoinFetchOrBatchRead.OUTER_JOIN && OrderListTestModel.this.joinFetchOrBatchRead != JoinFetchOrBatchRead.INNER_JOIN) {
if(indexesRead.size() != nExpected) {
localErrorMsg += attributeName + " Wrong number of objects read: " + indexesRead.size() +"; expected " + nExpected;
}
}
if(localErrorMsg.length() > 0) {
localErrorMsg = attributeName + ": " + localErrorMsg + "\n";
errorMsg += localErrorMsg;
}
}
/*
* all objects were constructed to incorporate the index, see createManager method.
*/
int getIndex(Object obj, String attributeName) {
int index = -1;
if(attributeName.equals("managedEmployees")) {
index = Integer.parseInt(((Employee)obj).getFirstName());
} else if(attributeName.equals("children")) {
index = Integer.parseInt(((Child)obj).getFirstName());
} else if(attributeName.equals("projects")) {
index = getNumberFromString(((Project)obj).getName());
} else if(attributeName.equals("responsibilitiesList")) {
index = getNumberFromString((String)obj);
} else if(attributeName.equals("phoneNumbers")) {
index = Integer.parseInt(((PhoneNumber)obj).getNumber());
}
return index;
}
int getNumberFromString(String str) {
int nEnd = str.length();
int nStart = 0;
for(nStart=0; nStart < nEnd; nStart++) {
char ch = str.charAt(nStart);
if('0' <= ch && ch <='9') {
break;
}
}
str = str.substring(nStart);
return Integer.parseInt(str);
}
@Override
protected void verify() {
if(errorMsg.length() > 0) {
errorMsg = "\n" + errorMsg;
throw new TestErrorException(errorMsg);
}
}
/*
* ManyToMany and DirectCollection mapping require table name expression.
* Used to test getField() expression (or getTable().getField() expression) equivalent to index().
*/
boolean isTableExpressionRequired(String attributeName) {
return attributeName.equals("projects") || attributeName.equals("responsibilitiesList");
}
/*
* ManyToMany and DirectCollection mapping require table name expression.
* Used to test getTable().getField() expression equivalent to index().
*/
String getTableName(String attributeName) {
if(attributeName.equals("projects")) {
return "OL_PROJ_EMP";
} else if(attributeName.equals("responsibilitiesList")) {
return "OL_RESPONS";
} else {
throw new TestProblemException(attributeName + " should not be looking for table name.");
}
}
/*
* Used to test getField() expression (or getTable().getField() expression) equivalent to index().
*/
String getFieldName(String attributeName) {
String fieldName = null;
if(attributeName.equals("managedEmployees")) {
fieldName = "MANAGED_ORDER";
} else if(attributeName.equals("children")) {
fieldName = "CHILDREN_ORDER";
} else if(attributeName.equals("projects")) {
fieldName = "PROJ_ORDER";
} else if(attributeName.equals("responsibilitiesList")) {
fieldName = "RESPONS_ORDER";
} else if(attributeName.equals("phoneNumbers")) {
fieldName = "PHONE_ORDER";
}
return fieldName;
}
}
/*
* For each mapping: add object in front, or in the middle, or to the end of the list.
* Currently only DirectCollectionMapping supports duplicates and nulls.
* The test still uses all mappings so that it could be run with INNER_JOIN.
*/
abstract class WhereToAddTest extends ChangeTest {
String whereToAdd;
WhereToAddTest(String whereToAdd) {
super();
this.whereToAdd = whereToAdd;
if(!front.equals(whereToAdd) && !middle.equals(whereToAdd) && !end.equals(whereToAdd)) {
throw new TestProblemException("Wrong whereToAdd = " + whereToAdd +"; Supported values: " + front +"; " +middle +"; " + end);
}
setName(getName() + " " + whereToAdd);
}
@Override
public void test() {
UnitOfWork uow = getSession().acquireUnitOfWork();
managerClone = (Employee)uow.registerObject(manager);
if(front.equals(whereToAdd)) {
addTo(managerClone, 0, objectToAdd());
} else if(middle.equals(whereToAdd)) {
addTo(managerClone, 1, objectToAdd());
} else if(end.equals(whereToAdd)) {
addTo(managerClone, objectToAdd());
}
uow.commit();
}
abstract List objectToAdd();
}
/*
* For each mapping: add null.
* Currently only DirectCollectionMapping supports nulls.
*/
class AddNullTest extends WhereToAddTest {
AddNullTest(String whereToAdd) {
super(whereToAdd);
}
@Override
List objectToAdd() {
List list = create("new", 0);
for(int i=0; i < list.size(); i++) {
if(list.get(i) instanceof String) {
// Currently only DirectCollectionMapping (responsibilities) supports nulls.
list.set(i, null);
break;
}
}
return list;
}
}
/*
* For each mapping: add duplicate.
* Currently only DirectCollectionMapping supports duplicates.
*/
class AddDuplicateTest extends WhereToAddTest {
AddDuplicateTest(String whereToAdd) {
super(whereToAdd);
}
@Override
List objectToAdd() {
List newList = create("new", 0);
List oldList = getFrom(managerClone, 0);
// the two lists are of the same size
for(int i=0; i < newList.size(); i++) {
if(newList.get(i) instanceof String) {
// Currently only DirectCollectionMapping (responsibilities) supports duplicates.
newList.set(i, oldList.get(i));
break;
}
}
return newList;
}
}
/*
* Break the order in the db, read the lists (with broken order) back, get the expected exception.
* Note that a separate test required for each mapping to make sure they all throw correct exception.
*/
abstract class BreakOrderExceptionTest extends ChangeTest {
BreakOrderExceptionTest() {
super();
if(orderCorrectionType != OrderCorrectionType.EXCEPTION) {
throw new TestProblemException("Requires OrderCorrectionType.EXCEPTION");
}
}
@Override
abstract public void test();
@Override
protected void verify() {
try {
super.verify();
throw new TestErrorException("Expected QueryException.LIST_ORDER_FIELD_WRONG_VALUE was not thrown.");
} catch (QueryException queryException) {
if(queryException.getErrorCode() == QueryException.LIST_ORDER_FIELD_WRONG_VALUE) {
// expected query exception on attempt to read broken order list
return;
} else {
throw queryException;
}
}
}
}
class BreakOrderExceptionTest_OneToMany extends BreakOrderExceptionTest {
BreakOrderExceptionTest_OneToMany() {
super();
}
@Override
public void test() {
breakManagedEmployeesOrder();
}
}
class BreakOrderExceptionTest_UnidirectionalOneToMany extends BreakOrderExceptionTest {
BreakOrderExceptionTest_UnidirectionalOneToMany() {
super();
}
@Override
public void test() {
breakChildrenOrder();
}
}
class BreakOrderExceptionTest_ManyToMany extends BreakOrderExceptionTest {
BreakOrderExceptionTest_ManyToMany() {
super();
}
@Override
public void test() {
breakProjectsOrder();
}
}
class BreakOrderExceptionTest_DirectCollection extends BreakOrderExceptionTest {
BreakOrderExceptionTest_DirectCollection() {
super();
}
@Override
public void test() {
breakResponsibilitiesOrder();
}
}
class BreakOrderExceptionTest_AggregateCollection extends BreakOrderExceptionTest {
BreakOrderExceptionTest_AggregateCollection() {
super();
}
@Override
public void test() {
breakPhonesOrder();
}
}
/*
* bug 331144: Removing from a collection that is broken results in an
* ArrayIndexOutOfBoundsException when the collection is fixed.
*/
class BreakOrderCorrectionAndRemoveTest extends BreakOrderCorrectionTest {
BreakOrderCorrectionAndRemoveTest(boolean shoulReadManagerThroughUow) {
super(shoulReadManagerThroughUow);
}
@Override
public void test() {
breakOrder();
// clear cache to force reading back the objects
getSession().getIdentityMapAccessor().initializeAllIdentityMaps();
if(!shoulReadManagerThroughUow) {
// read in the object with broken order in the db
manager = (Employee)getSession().readObject(manager);
if(isInstantiated(manager)) {
// verify that all attribute values are marked as having broken order
errorMsg = this.verifyIsListOrderBrokenInDb(manager, true);
if(errorMsg.length() > 0) {
errorMsg = "manager in test: " + errorMsg;
throw new TestErrorException(errorMsg);
}
}
}
UnitOfWork uow = getSession().acquireUnitOfWork();
if(shoulReadManagerThroughUow) {
managerClone = (Employee)uow.readObject(manager);
} else {
managerClone = (Employee)uow.registerObject(manager);
}
// remove element 1
List list1 = removeFrom(managerClone, 1);
// verify that all attribute values are marked as having broken order,
// at this point they should be instantiated
errorMsg = this.verifyIsListOrderBrokenInDb(managerClone, true);
if(errorMsg.length() > 0) {
errorMsg = "managerClone in test: " + errorMsg;
uow.release();
throw new TestErrorException(errorMsg);
}
try {
uow.commit();
} catch (ArrayIndexOutOfBoundsException exception){
throw new TestErrorException("Test received exception commit when fixing a broken collection", exception);
}
}
}
/*
* Break the order in the db, read the lists (with broken order) back, update lists, write out updated lists.
* For verification temporary switch container policy into EXCEPTION mode, read back the list -
* exception thrown if the order is still broken.
*/
class BreakOrderCorrectionTest extends ChangeTest {
boolean shoulReadManagerThroughUow;
BreakOrderCorrectionTest(boolean shoulReadManagerThroughUow) {
super();
this.nSize = 4;
this.shoulReadManagerThroughUow = shoulReadManagerThroughUow;
if(orderCorrectionType != OrderCorrectionType.READ_WRITE) {
throw new TestProblemException("Requires OrderCorrectionType.CORRECTION");
}
setName(getName() + (shoulReadManagerThroughUow ? " ReadThroughUow" : " ReadThroughSession"));
}
@Override
public void test() {
breakOrder();
// clear cache to force reading back the objects
getSession().getIdentityMapAccessor().initializeAllIdentityMaps();
if(!shoulReadManagerThroughUow) {
// read in the object with broken order in the db
manager = (Employee)getSession().readObject(manager);
if(isInstantiated(manager)) {
// verify that all attribute values are marked as having broken order
errorMsg = this.verifyIsListOrderBrokenInDb(manager, true);
if(errorMsg.length() > 0) {
errorMsg = "manager in test: " + errorMsg;
throw new TestErrorException(errorMsg);
}
}
}
// change the order of elements 0 <-> 1
UnitOfWork uow = getSession().acquireUnitOfWork();
if(shoulReadManagerThroughUow) {
managerClone = (Employee)uow.readObject(manager);
} else {
managerClone = (Employee)uow.registerObject(manager);
}
List list1 = removeFrom(managerClone, 1);
// verify that all attribute values are marked as having broken order,
// at this point they should be instantiated
errorMsg = this.verifyIsListOrderBrokenInDb(managerClone, true);
if(errorMsg.length() > 0) {
errorMsg = "managerClone in test: " + errorMsg;
uow.release();
throw new TestErrorException(errorMsg);
}
List list0 = removeFrom(managerClone, 0);
addTo(managerClone, list1);
addTo(managerClone, list0);
uow.commit();
}
@Override
protected void verify() {
OrderCorrectionType originalMode = this.changeOrderCorrectionType(OrderCorrectionType.EXCEPTION);
try {
if(shoulReadManagerThroughUow) {
// manager is not in shared cache - bring the one from the shared cache.
ReadObjectQuery query = new ReadObjectQuery();
query.setReferenceClass(Employee.class);
query.checkCacheOnly();
manager = (Employee)getSession().readObject(manager);
}
// verify that all attribute values are marked as having NOT broken order
errorMsg = this.verifyIsListOrderBrokenInDb(manager, false);
if(errorMsg.length() > 0) {
errorMsg = "manager in verify: " + errorMsg;
}
String localErrorMsg = this.verifyIsListOrderBrokenInDb(managerClone, false);
if(localErrorMsg.length() > 0) {
localErrorMsg = "managerClone in verify: " + localErrorMsg;
errorMsg += localErrorMsg;
}
super.verify();
} finally {
this.changeOrderCorrectionType(originalMode);
}
}
}
class BaseMultipleManagersTest extends BaseTest {
/* number of managers created by createManagers method.*/
int nManagers;
/*
* Number of each manager's managedEmployees, children, projects, responsibilities, phones
* created by createManager method.
*/
int nSize;
/* managers */
List<Employee> managers = new ArrayList(nManagers);
/* their uow clones */
List<Employee> managerClones = new ArrayList(nManagers);
BaseMultipleManagersTest() {
super();
nManagers = 2;
nSize = 2;
}
/*
* Creates manager with nSize members
*/
void createManagers() {
UnitOfWork uow = getSession().acquireUnitOfWork();
for(int i=0; i < nManagers; i++) {
String iStr = Integer.toString(i);
Employee manager = new Employee("Manager", iStr);
for(int j=0; j < nSize; j++) {
addTo(manager, create(iStr, j));
}
Employee managerClone = (Employee)uow.registerObject(manager);
managers.add(manager);
managerClones.add(managerClone);
}
uow.commit();
}
@Override
public void reset() {
super.reset();
managers.clear();
managerClones.clear();
}
@Override
public void verify() {
if(managers == null || managers.isEmpty()) {
throw new TestErrorException("managers is null or empty. Nothing to verify");
}
String textNameExt;
for(int k=0; k<2; k++) {
if(k == 0) {
textNameExt = "Cache";
} else {
textNameExt = "DB";
// Read back the inserted objects, make sure the order is correct.
getSession().getIdentityMapAccessor().initializeAllIdentityMaps();
ReadAllQuery query = new ReadAllQuery();
query.setReferenceClass(Employee.class);
Expression exp = query.getExpressionBuilder().get("firstName").equal("Manager");
query.setSelectionCriteria(exp);
query.addOrdering(query.getExpressionBuilder().get("lastName"));
managers.clear();
managers.addAll((List<Employee>)getSession().executeQuery(query));
}
if(managers.size() != managerClones.size()) {
errorMsg = "wrong managers size " + managers.size() + "; expected size is " + managerClones.size();
} else {
for(int i=0; i<managers.size(); i++) {
if(!getAbstractSession().compareObjects(managerClones.get(i), managers.get(i))) {
String localErrorMsg = textNameExt + ": " + "manager["+i+"] not equal\n";
errorMsg += localErrorMsg;
}
}
}
}
if(errorMsg.length() > 0) {
throw new TestErrorException(errorMsg);
}
}
}
/*
* Creates managers, verifies that they were correctly merged into cache and written to the db.
*/
class CreateManagersTest extends BaseMultipleManagersTest {
CreateManagersTest() {
super();
}
@Override
public void setup() {
createManagers();
}
}
/*
* Creates empty managers, verifies that they were correctly merged into cache and written to the db.
* For OUTER_JOIN case to verify that read back lists are empty (as opposed to having null members).
*/
class CreateEmptyManagersTest extends CreateManagersTest {
CreateEmptyManagersTest() {
super();
nSize = 0;
}
}
/*
* Verify that for each mapping both its container policy and its select query's container policy
* are of the expected type.
*/
class VerifyContainerPolicyClassTest extends TestCase {
Class expectedClass;
VerifyContainerPolicyClassTest() {
this(NullsLastOrderedListContainerPolicy.class);
}
VerifyContainerPolicyClassTest(Class expectedClass) {
super();
this.expectedClass = expectedClass;
setName("VerifyContainerPolicyClassTest");
}
@Override
public void verify() {
String errorMsg = "";
List<CollectionMapping> listOrderMappings = EmployeeSystem.getListOrderMappings(getDatabaseSession());
for(int i=0; i < listOrderMappings.size(); i++) {
CollectionMapping mapping = listOrderMappings.get(i);
if(!mapping.getContainerPolicy().getClass().equals(expectedClass)) {
errorMsg += mapping.getAttributeName() + ".containerPolicy type is wrong; ";
}
ReadQuery selectQuery = mapping.getSelectionQuery();
if(selectQuery.isReadAllQuery()) {
if(!((ReadAllQuery)selectQuery).getContainerPolicy().getClass().equals(expectedClass)) {
errorMsg += mapping.getAttributeName() + ".queryContainerPolicy type is wrong; ";
}
} else {
if(!((DataReadQuery)selectQuery).getContainerPolicy().getClass().equals(expectedClass)) {
errorMsg += mapping.getAttributeName() + ".queryContainerPolicy type is wrong; ";
}
}
}
if(errorMsg.length() > 0) {
throw new TestErrorException(errorMsg);
}
}
}
/*
* Test for OneToMany and UnideirectionalOneToMany mappings only:
* verify that the row in the db corresponding to the removed object
* has its order field value set to null.
*/
class VerifyForeignKeyOfRemovedObject extends ChangeTest {
boolean deleteSourceObject;
VerifyForeignKeyOfRemovedObject(boolean deleteSourceObject) {
super();
this.deleteSourceObject = deleteSourceObject;
setName(getShortClassName() + (deleteSourceObject ? "_deleteSource" : "_removeTarget"));
}
// Only tests OneToMany and UnideirectionalOneToMany
@Override
public void setup() {
this.usePhones = false;
this.useProjects = false;
this.useResponsibilities = false;
super.setup();
}
@Override
public void test() {
UnitOfWork uow = getSession().acquireUnitOfWork();
managerClone = (Employee)uow.registerObject(manager);
if(deleteSourceObject) {
for(int i=0; i < nSize; i++) {
if(useManagedEmployees) {
managerClone.getManagedEmployees().get(i).setManager(null);
}
}
uow.deleteObject(managerClone);
} else {
for(int i=nSize-1; 0 <= i; i--) {
if(useManagedEmployees) {
managerClone.removeManagedEmployee(i);
}
if(useChildren) {
managerClone.getChildren().remove(i);
}
}
}
uow.commit();
}
@Override
public void verify() {
// assume (as usual) that there were no objects in the db when the test started,
// then all the objects left should have their order set to null (including manager if not deleted).
if(useManagedEmployees) {
String sqlString = "SELECT COUNT(*) FROM "+this.getManagegedEmployeesOrderTable()+" WHERE "+this.getManagegedEmployeesOrderField()+" IS NOT NULL";
int nonNulls = ((Number)((AbstractRecord)getSession().executeSQL(sqlString).get(0)).getValues().get(0)).intValue();
if(nonNulls != 0) {
errorMsg += "useManagedEmployees has "+nonNulls+" non nulls; ";
}
}
if(useChildren) {
String sqlString = "SELECT COUNT(*) FROM "+this.getChildrenOrderTable()+" WHERE "+this.getChildrenOrderField()+" IS NOT NULL";
int nonNulls = ((Number)((AbstractRecord)getSession().executeSQL(sqlString).get(0)).getValues().get(0)).intValue();
if(nonNulls != 0) {
errorMsg += "useManagedEmployees has "+nonNulls+" non nulls; ";
}
}
}
}
}