| /* |
| * 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()); |
| } |
| } |
| |
| } |
| |