blob: 20adf97b6ad3f669705c86b16c80ac68550334b3 [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.unitofwork.transactionisolation;
import java.util.Vector;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.ReadAllQuery;
import org.eclipse.persistence.sessions.SessionEvent;
import org.eclipse.persistence.sessions.SessionEventAdapter;
import org.eclipse.persistence.sessions.UnitOfWork;
import org.eclipse.persistence.testing.framework.AutoVerifyTestCase;
import org.eclipse.persistence.testing.framework.TestErrorException;
import org.eclipse.persistence.testing.models.employee.domain.Address;
import org.eclipse.persistence.testing.models.employee.domain.Employee;
/**
* Tests the Session read refactoring / reading through the write connection
* properly feature.
* <p>
* This test declares the expected behavior of triggering batch valueholders
* when a UnitOfWork is in an early transaction.
* <p>
* It is not nice that we are cloning every batch read object into the
* UnitOfWork, a sideffect of executing the batch query in the UnitOfWork.
* <p>
* Also must not put the batched objects in the query, but store them instead
* on the UnitOfWork itself.
* <p>
* There is an optimization where even if we are in transaction, if the wrapped
* valueholder is a session valueholder (not private to the unit of work), then
* we can just trigger that one instead.
* <p>
* As for all valueholder tests, triggering the clone in transaction should
* not trigger the wrapped valueholder.
* @author smcritch
* @since release specific (what release of product did this appear in)
*/
public class
TransactionIsolationBatchReadingTest extends AutoVerifyTestCase {
UnitOfWork unitOfWork;
@Override
protected void setup() throws Exception {
getSession().getIdentityMapAccessor().initializeAllIdentityMaps();
unitOfWork = getSession().acquireUnitOfWork();
}
@Override
public void reset() throws Exception {
if (unitOfWork != null) {
getSession().getIdentityMapAccessor().initializeAllIdentityMaps();
unitOfWork.release();
unitOfWork = null;
}
}
@Override
public void test() {
ReadAllQuery query = new ReadAllQuery(Employee.class);
query.addBatchReadAttribute("address");
Expression maleExp = (new ExpressionBuilder()).get("gender").equal("Male");
Expression femaleExp = (new ExpressionBuilder()).get("gender").equal("Female");
query.setSelectionCriteria(maleExp);
Vector males = (Vector)getSession().executeQuery(query);
Employee originalMale = (Employee)males.elementAt(0);
Employee male = (Employee)unitOfWork.registerObject(originalMale);
// read the males before early transaction started and the females after.
unitOfWork.beginEarlyTransaction();
query.setSelectionCriteria(femaleExp);
Vector females = (Vector)unitOfWork.executeQuery(query);
Employee female = (Employee)females.elementAt(0);
Address maleAddress = male.getAddress();
Address femaleAddress = female.getAddress();
strongAssert((maleAddress != null), "The batch read attribute [male.address] was null");
strongAssert((femaleAddress != null), "The batch read attribute [female.address] was null");
strongAssert(male.address.isInstantiated(),
"The wrapped valueholder should be instantiated, because it " + "was read on the session and is safe to trigger.");
strongAssert(((UnitOfWorkImpl)unitOfWork).getBatchQueries() != null,
"unitOfWork.getBatchReadObjects() must never return null");
strongAssert(((UnitOfWorkImpl)unitOfWork).getBatchQueries().size() == 2,
"unitOfWork batchReadObjects should only be storing the female addresses");
if (!((UnitOfWorkImpl)unitOfWork).getBatchQueries().isEmpty()) {
DatabaseQuery batchQuery =
((UnitOfWorkImpl)unitOfWork).getBatchQueries().keySet().iterator().next();
strongAssert(batchQuery.getBatchObjects() == null,
"triggering batch query on UOW should not store batched objects on the query.");
}
strongAssert((originalMale.getAddress() != maleAddress),
"Triggering a valueholder on session is returning a clone from " +
"the UnitOfWork batched objects.");
Employee otherFemale = (Employee)females.elementAt(1);
// Tests that batch querying actually works when in transaction.
QueryCatcher queryCatcher = new QueryCatcher();
unitOfWork.getEventManager().addListener(queryCatcher);
try {
otherFemale.getAddress();
} finally {
unitOfWork.getEventManager().removeListener(queryCatcher);
}
}
public class QueryCatcher extends SessionEventAdapter {
/**
* PUBLIC:
* This event is raised before the execution of every query against the session.
* The event contains the query to be executed.
*/
@Override
public void preExecuteQuery(SessionEvent event) {
throw new TestErrorException("Triggering a second batch valueholder of the same batch query is going to the database. Query: " +
event.getQuery());
}
}
}