/******************************************************************************* * Copyright (c) 1998, 2015 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 v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * 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 extends QueryBasedValueHolder { 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. */ protected Object instantiate(AbstractSession session) throws EclipseLinkException { return 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. */ public Object instantiateForUnitOfWorkValueHolder(UnitOfWorkValueHolder 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 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. */ public boolean isEasilyInstantiated() { return this.isInstantiated; } /** * Reset all the fields that are not needed after instantiation. */ protected void resetFields() { super.resetFields(); this.mapping = null; this.originalQuery = null; } protected void setMapping(ForeignReferenceMapping mapping) { this.mapping = mapping; } }