| /* |
| * 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.internal.indirection; |
| |
| import org.eclipse.persistence.internal.identitymaps.CacheKey; |
| import org.eclipse.persistence.internal.sessions.AbstractRecord; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl; |
| import org.eclipse.persistence.mappings.*; |
| import org.eclipse.persistence.queries.*; |
| import org.eclipse.persistence.exceptions.*; |
| |
| /** |
| * BatchValueHolder is used by the mappings that use indirection when using query optimization (batch reading). |
| * This value holder is different from QueryBasedValueHolder in that its value must be extracted from one of the |
| * results return by the query, not the entire result. |
| * The query is also shared by all other value holders within the batch and it must be ensured that the query is only |
| * executed once. Concurrency must also be maintained across all of the value holders in the batch. |
| */ |
| public class BatchValueHolder<T> extends QueryBasedValueHolder<T> { |
| protected transient ForeignReferenceMapping mapping; |
| protected transient ObjectLevelReadQuery originalQuery; |
| protected transient CacheKey parentCacheKey; |
| |
| /** |
| * Initialize the query-based value holder. |
| * @param query The query that returns the object when executed. |
| * @param row The row representation of the object. |
| * @param mapping The mapping that is uses batch reading. |
| */ |
| public BatchValueHolder(ReadQuery query, AbstractRecord row, ForeignReferenceMapping mapping, ObjectLevelReadQuery originalQuery, CacheKey parentCacheKey) { |
| super(query, row, originalQuery.getSession()); |
| this.mapping = mapping; |
| this.originalQuery = originalQuery; |
| this.parentCacheKey = parentCacheKey; |
| } |
| |
| protected ForeignReferenceMapping getMapping() { |
| return mapping; |
| } |
| |
| /** |
| * Instantiate the object by having the mapping extract its value from the query. |
| * Concurrency must be maintained across all of the value holders, |
| * since they all share the same query, the extractResultFromBatchQuery method must be synchronized. |
| */ |
| @Override |
| @SuppressWarnings({"unchecked"}) |
| protected T instantiate(AbstractSession session) throws EclipseLinkException { |
| return (T) this.mapping.extractResultFromBatchQuery(this.query, this.parentCacheKey, this.row, session, this.originalQuery); |
| } |
| |
| /** |
| * Triggers UnitOfWork valueholders directly without triggering the wrapped |
| * valueholder (this). |
| * <p> |
| * When in transaction and/or for pessimistic locking the |
| * UnitOfWorkValueHolder needs to be triggered directly without triggering |
| * the wrapped valueholder. However only the wrapped valueholder knows how |
| * to trigger the indirection, i.e. it may be a batchValueHolder, and it |
| * stores all the info like the row and the query. Note: This method is not |
| * thread-safe. It must be used in a synchronized manner. |
| * The batch value holder must use a batch query relative to the unit of work, |
| * as the batch is local to the unit of work. |
| */ |
| @Override |
| @SuppressWarnings({"unchecked"}) |
| public T instantiateForUnitOfWorkValueHolder(UnitOfWorkValueHolder<T> unitOfWorkValueHolder) { |
| UnitOfWorkImpl unitOfWork = unitOfWorkValueHolder.getUnitOfWork(); |
| ReadQuery localQuery = unitOfWork.getBatchQueries().get(this.query); |
| if (localQuery == null) { |
| localQuery = (ReadQuery)this.query.clone(); |
| unitOfWork.getBatchQueries().put(this.query, localQuery); |
| } |
| return (T) this.mapping.extractResultFromBatchQuery(localQuery, this.parentCacheKey, this.row, unitOfWorkValueHolder.getUnitOfWork(), this.originalQuery); |
| } |
| |
| /** |
| * INTERNAL: |
| * Answers if this valueholder is easy to instantiate. |
| * @return true if getValue() won't trigger a database read. |
| */ |
| @Override |
| public boolean isEasilyInstantiated() { |
| return this.isInstantiated; |
| } |
| |
| /** |
| * Reset all the fields that are not needed after instantiation. |
| */ |
| @Override |
| protected void resetFields() { |
| super.resetFields(); |
| this.mapping = null; |
| this.originalQuery = null; |
| } |
| |
| protected void setMapping(ForeignReferenceMapping mapping) { |
| this.mapping = mapping; |
| } |
| } |