blob: 6d723d3db95e42cc52582cbe959312a381e260d3 [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.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;
}
}