All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.persistence.internal.indirection.DatabaseValueHolder Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1998, 2024 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.exceptions.DatabaseException;
import org.eclipse.persistence.indirection.ValueHolderInterface;
import org.eclipse.persistence.indirection.WeavedAttributeValueHolderInterface;
import org.eclipse.persistence.internal.localization.ToStringLocalization;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;

import java.io.Serializable;

/**
 * DatabaseValueHolder wraps a database-stored object and implements
 * behavior to access it. The object is read only once from database
 * after which is cached for faster access.
 *
 * @see ValueHolderInterface
 * @author    Dorin Sandu
 */
public abstract class DatabaseValueHolder implements WeavedAttributeValueHolderInterface, Cloneable, Serializable {

    /** Stores the object after it is read from the database. */
    protected volatile T value;

    /** Indicates whether the object has been read from the database or not. */
    protected volatile boolean isInstantiated;

    /** Stores the session for the database that contains the object. */
    protected transient AbstractSession session;

    /** Stores the row representation of the object. */
    // Cannot be transient as may be required to extract the pk from a serialized object.
    protected AbstractRecord row;

    /**
     * The variable below is used as part of the implementation of WeavedAttributeValueHolderInterface
     * It is used to track whether a valueholder that has been weaved into a class is coordinated
     * with the underlying property
     * Set internally in EclipseLink when the state of coordination between a weaved valueholder and the underlying property is known
     */
    protected boolean isCoordinatedWithProperty = false;

    /**
     * Default constructor.
     */
    protected DatabaseValueHolder() {
    }

    @Override
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException exception) {
            throw new InternalError();
        }
    }

    /**
     * Return the row.
     */
    public AbstractRecord getRow() {
        return row;
    }

    /**
     * Return the session.
     */
    public AbstractSession getSession() {
        return session;
    }

    public ValueHolderInterface getWrappedValueHolder() {
        return null;
    }

    /**
     * Return the object.
     */
    @Override
    public T getValue() {
        boolean instantiated = this.isInstantiated;
        if (!instantiated) {
            synchronized (this) {
                instantiated = this.isInstantiated;
                if (!instantiated) {
                    // The value must be set directly because the setValue can also cause instantiation under UOW.
                    privilegedSetValue(instantiate());
                    this.isInstantiated = true;
                    postInstantiate();
                    resetFields();
                }
            }
        }
        return value;
    }

    /**
     * Process against the UOW and attempt to load a local copy before going to the shared cache
     * If null is returned then the calling UOW will instantiate as normal.
     */
    public T getValue(UnitOfWorkImpl uow) {
        //This method simply returns null as this will cause the UOWVH to trigger
        //the relationship normally.
        return null;
    }

    /**
     * Instantiate the object.
     */
    protected abstract T instantiate() throws DatabaseException;

    /**
     * Triggers UnitOfWork valueholders directly without triggering the wrapped
     * valueholder (this).
     * 

* 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: Implementations of this method are not necessarily thread-safe. They must * be used in a synchronized manner */ public abstract T instantiateForUnitOfWorkValueHolder(UnitOfWorkValueHolder unitOfWorkValueHolder); /** * This method is used as part of the implementation of WeavedAttributeValueHolderInterface * It is used to check whether a valueholder that has been weaved into a class is coordinated * with the underlying property */ @Override public boolean isCoordinatedWithProperty(){ return isCoordinatedWithProperty; } /** * This method is used as part of the implementation of WeavedAttributeValueHolderInterface. *

* A DatabaseValueHolder is set up by TopLink and will never be a newly weaved valueholder. * As a result, this method is stubbed out. */ @Override public boolean isNewlyWeavedValueHolder(){ return false; } /** * 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; } /** * Return a boolean indicating whether the object * has been read from the database or not. */ @Override public boolean isInstantiated() { return isInstantiated; } /** * Answers if this valueholder is a pessimistic locking one. Such valueholders * are special in that they can be triggered multiple times by different * UnitsOfWork. Each time a lock query will be issued. Hence even if * instantiated it may have to be instantiated again, and once instantiated * all fields can not be reset. * Note: Implementations of this method are not necessarily thread-safe. They must * be used in a synchronizaed manner */ public abstract boolean isPessimisticLockingValueHolder(); /** * Answers if this valueholder is referenced only by a UnitOfWork valueholder. * I.e. it was built in valueFromRow which was called by buildCloneFromRow. *

* Sometimes in transaction a UnitOfWork clone, and all valueholders, are built * directly from the row; however a UnitOfWorkValueHolder does not know how to * instantiate itself so wraps this which does. *

* On a successful merge must be released to the session cache with * releaseWrappedValueHolder. */ protected boolean isTransactionalValueHolder() { return ((session != null) && session.isUnitOfWork()); } /** * Used to determine if this is a remote uow value holder that was serialized to the server. * It has no reference to its wrapper value holder, so must find its original object to be able to instantiate. */ public boolean isSerializedRemoteUnitOfWorkValueHolder() { return false; } /** * INTERNAL: * Run any extra code required after the valueholder instantiates * @see QueryBasedValueHolder */ public void postInstantiate(){ //noop } /** * Set the object. This is used only by the privileged methods. One must be very careful in using this method. */ public void privilegedSetValue(T value) { this.value = value; isCoordinatedWithProperty = false; } /** * Releases a wrapped valueholder privately owned by a particular unit of work. *

* When unit of work clones are built directly from rows no object in the shared * cache points to this valueholder, so it can store the unit of work as its * session. However once that UnitOfWork commits and the valueholder is merged * into the shared cache, the session needs to be reset to the root session, ie. * the server session. */ public void releaseWrappedValueHolder(AbstractSession targetSession) { AbstractSession session = this.session; if ((session != null) && session.isUnitOfWork()) { this.session = targetSession; } } /** * Reset all the fields that are not needed after instantiation. */ protected void resetFields() { this.row = null; this.session = null; } /** * This method is used as part of the implementation of WeavedAttributeValueHolderInterface * It is used internally by EclipseLink to set whether a valueholder that has been weaved into a class is coordinated * with the underlying property */ @Override public void setIsCoordinatedWithProperty(boolean coordinated){ this.isCoordinatedWithProperty = coordinated; } /** * This method is used as part of the implementation of WeavedAttributeValueHolderInterface *

* A DatabaseValueHolder is set up by EclipseLink and will never be a newly weaved valueholder * As a result, this method is stubbed out. */ @Override public void setIsNewlyWeavedValueHolder(boolean isNew){ } /** * Set the instantiated flag to true. */ public void setInstantiated() { isInstantiated = true; } /** * Set the row. */ public void setRow(AbstractRecord row) { this.row = row; } /** * Set the session. */ public void setSession(AbstractSession session) { this.session = session; } /** * Set the instantiated flag to false. */ public void setUninstantiated() { isInstantiated = false; } /** * Set the object. */ @Override public void setValue(T value) { this.value = value; setInstantiated(); } /** * INTERNAL: * Return if add/remove should trigger instantiation or avoid. * Current instantiation is avoided is using change tracking. */ @Override public boolean shouldAllowInstantiationDeferral() { return true; } @Override public String toString() { if (isInstantiated()) { return "{" + getValue() + "}"; } else { return "{" + getClass().getSimpleName() + ": " + ToStringLocalization.buildMessage("not_instantiated", null) + "}"; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy