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

org.eclipse.persistence.internal.descriptors.CascadeLockingPolicy 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.descriptors;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.InheritancePolicy;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.internal.expressions.SQLSelectStatement;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.ObjectChangeSet;
import org.eclipse.persistence.internal.sessions.UnitOfWorkChangeSet;
import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.queries.DataReadQuery;
import org.eclipse.persistence.queries.ReadObjectQuery;
import org.eclipse.persistence.sessions.DatabaseRecord;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * INTERNAL:
 */
public class CascadeLockingPolicy {
    protected Class m_parentClass;
    protected ReadObjectQuery m_query;
    protected ClassDescriptor m_descriptor;
    protected ClassDescriptor m_parentDescriptor;
    protected Map m_queryKeyFields;
    protected Map m_mappedQueryKeyFields;
    protected Map m_unmappedQueryKeyFields;
    protected DatabaseMapping m_parentMapping;
    protected boolean m_lookForParentMapping;
    protected boolean m_shouldHandleUnmappedFields;
    protected boolean m_hasCheckedForUnmappedFields;
    protected DataReadQuery m_unmappedFieldsQuery;

    /**
     * INTERNAL:
     */
    public CascadeLockingPolicy(ClassDescriptor parentDescriptor, ClassDescriptor descriptor) {
        m_descriptor = descriptor;
        m_parentDescriptor = parentDescriptor;
        m_parentClass = m_parentDescriptor.getJavaClass();
    }

    /**
     * INTERNAL:
     */
    protected ReadObjectQuery getQuery() {
        if (m_query == null) {
            m_query = new ReadObjectQuery(m_parentClass);

            Expression selectionCriteria = null;
            Iterator keys = m_queryKeyFields.keySet().iterator();
            ExpressionBuilder builder = new ExpressionBuilder();

            while (keys.hasNext()) {
                String keyField = keys.next().getQualifiedName();

                if (selectionCriteria == null) {
                    selectionCriteria = builder.getField(keyField).equal(builder.getParameter(keyField));
                } else {
                    selectionCriteria.and(builder.getField(keyField).equal(builder.getParameter(keyField)));
                }

                m_query.addArgument(keyField);
            }

            m_query.setSelectionCriteria(selectionCriteria);
            m_query.setShouldUseWrapperPolicy(false);
        }

        return m_query;
    }

    /**
     * INTERNAL:
     */
     protected DatabaseMapping getParentMapping() {
        // If the query is null, then we have not been initialized. Try to
        // look up a parent mapping first if we have lookup fields. For a
        // 1-M we can not perform the getMappingForField until the fields
        // have been initialized.
        // If the parent mapping is not found, a query will be initialized
        // and the following lookup will no longer hit.
        if (m_parentMapping == null && m_lookForParentMapping && m_query == null) {
            Iterator itFields = m_queryKeyFields.values().iterator();
            while(itFields.hasNext()) {
                DatabaseMapping mapping = m_descriptor.getObjectBuilder().getMappingForField(itFields.next());

                if(mapping == null) {
                    // at least one field is not mapped therefore no parent mapping exists.
                    m_parentMapping = null;
                    break;
                } else if(mapping.isObjectReferenceMapping()) {
                    if(m_parentMapping == null) {
                        m_parentMapping = mapping;
                    } else {
                        if(m_parentMapping != mapping) {
                            // there's more than one mapping therefore no parent mapping exists.
                            m_parentMapping = null;
                            break;
                        }
                    }
                }
            }
        }

        return m_parentMapping;
     }

     /**
      * Get the descriptor that really represents this object
      * In the case of inheritance, the object may represent a subclass of class the descriptor
      * represents.
      * 

* If there is no InheritancePolicy, we return our parentDescriptor * If there is inheritance we will search for a descriptor that represents parentObj and * return that descriptor */ protected ClassDescriptor getParentDescriptorFromInheritancePolicy(Object parentObj){ ClassDescriptor realParentDescriptor = m_parentDescriptor; if (realParentDescriptor.hasInheritance()){ InheritancePolicy inheritancePolicy = realParentDescriptor.getInheritancePolicy(); ClassDescriptor childDescriptor = inheritancePolicy.getDescriptor(parentObj.getClass()); if (childDescriptor != null){ realParentDescriptor = childDescriptor; } } return realParentDescriptor; } /** * INTERNAL: */ protected AbstractRecord getMappedTranslationRow(Object changedObj, UnitOfWorkImpl uow) { AbstractRecord translationRow = new DatabaseRecord(); Iterator> it = m_mappedQueryKeyFields.entrySet().iterator(); while(it.hasNext()) { Map.Entry entry = it.next(); Object value = m_descriptor.getObjectBuilder().extractValueFromObjectForField(changedObj, entry.getValue(), uow); translationRow.add(entry.getKey(), value); } return translationRow; } /** * INTERNAL: */ protected AbstractRecord getUnmappedTranslationRow(Object changedObj, UnitOfWorkImpl uow) { AbstractRecord unmappedFieldsQueryTranslationRow = new DatabaseRecord(); Iterator itPrimaryKey = m_descriptor.getPrimaryKeyFields().iterator(); while (itPrimaryKey.hasNext()) { DatabaseField primaryKey = itPrimaryKey.next(); Object value = m_descriptor.getObjectBuilder().extractValueFromObjectForField(changedObj, primaryKey, uow); unmappedFieldsQueryTranslationRow.add(primaryKey, value); } List result = (List)uow.executeQuery(m_unmappedFieldsQuery, unmappedFieldsQueryTranslationRow); if(result == null || result.isEmpty()) { // the object is not in the db return null; } AbstractRecord unmappedValues = (AbstractRecord)result.get(0); AbstractRecord translationRow = new DatabaseRecord(); Iterator> it = m_unmappedQueryKeyFields.entrySet().iterator(); while(it.hasNext()) { Map.Entry entry = it.next(); Object value = unmappedValues.get(entry.getValue()); translationRow.add(entry.getKey(), value); } return translationRow; } /** * INTERNAL: * Identify mapped and not mapped fields (should be done once). * The result - either two non-empty Maps m_unmappedQueryKeyFields and m_mappedQueryKeyFields, * or m_unmappedQueryKeyFields == null and m_mappedQueryKeyFields == m_queryKeyFields. */ public void initUnmappedFields(UnitOfWorkImpl uow) { if(!m_hasCheckedForUnmappedFields) { m_mappedQueryKeyFields = new HashMap<>(); m_unmappedQueryKeyFields = new HashMap<>(); Iterator> it = m_queryKeyFields.entrySet().iterator(); while(it.hasNext()) { Map.Entry entry = it.next(); if(m_descriptor.getObjectBuilder().getMappingForField(entry.getValue()) == null) { m_unmappedQueryKeyFields.put(entry.getKey(), entry.getValue()); } else { m_mappedQueryKeyFields.put(entry.getKey(), entry.getValue()); } } if(m_unmappedQueryKeyFields.isEmpty()) { m_unmappedQueryKeyFields = null; m_mappedQueryKeyFields = m_queryKeyFields; } initUnmappedFieldsQuery(uow); m_hasCheckedForUnmappedFields = true; } } /** * INTERNAL: * This method called in case there are m_unmappedQueryKeyFields. * It creates a query that would fetch the values for this fields from the db. */ public void initUnmappedFieldsQuery(UnitOfWorkImpl uow) { if(m_unmappedFieldsQuery == null) { m_unmappedFieldsQuery = new DataReadQuery(); Expression whereClause = null; Expression builder = new ExpressionBuilder(); Iterator itPrimaryKey = m_descriptor.getPrimaryKeyFields().iterator(); while (itPrimaryKey.hasNext()) { DatabaseField primaryKey = itPrimaryKey.next(); Expression expression = builder.getField(primaryKey).equal(builder.getParameter(primaryKey)); whereClause = expression.and(whereClause); m_unmappedFieldsQuery.addArgument(primaryKey.getQualifiedName()); } SQLSelectStatement statement = new SQLSelectStatement(); Iterator itUnmappedFields = m_unmappedQueryKeyFields.values().iterator(); while (itUnmappedFields.hasNext()) { DatabaseField field = itUnmappedFields.next(); statement.addField(field); } statement.setWhereClause(whereClause); statement.normalize(uow.getParent(), m_descriptor); m_unmappedFieldsQuery.setSQLStatement(statement); m_unmappedFieldsQuery.setSessionName(m_descriptor.getSessionName()); } } /** * INTERNAL: */ public void lockNotifyParent(Object obj, UnitOfWorkChangeSet changeSet, UnitOfWorkImpl uow) { Object parentObj = null; // Check for a parent object via the parent (back pointer) mapping first. DatabaseMapping parentMapping = getParentMapping(); if (parentMapping != null && parentMapping.isObjectReferenceMapping()) { parentObj = parentMapping.getRealAttributeValueFromObject(obj, uow); } // If the parent object is still null at this point, try a query. // check out why no query keys. if (parentObj == null) { AbstractRecord translationRow; if(m_shouldHandleUnmappedFields) { // should look for unmapped fields. initUnmappedFields(uow); if(m_unmappedQueryKeyFields != null) { // there are some unmapped fields - fetch the values for the from the db. AbstractRecord unmappedTranslationRow = getUnmappedTranslationRow(obj, uow); if(unmappedTranslationRow == null) { // the object is not yet in the db return; } else { // merge mapped and unmapped values into the single translation row. translationRow = getMappedTranslationRow(obj, uow); translationRow.putAll(unmappedTranslationRow); } } else { // no unmapped fields translationRow = getMappedTranslationRow(obj, uow); } } else { // no unmapped fields translationRow = getMappedTranslationRow(obj, uow); } // the query is set to return an unwrapped object. parentObj = uow.executeQuery(getQuery(), translationRow); } else { // make sure the parent object is unwrapped. if (m_parentDescriptor.hasWrapperPolicy()) { m_parentDescriptor.getWrapperPolicy().unwrapObject(parentObj, uow); } } ClassDescriptor realParentDescriptor = m_parentDescriptor; if (parentObj != null){ realParentDescriptor = getParentDescriptorFromInheritancePolicy(parentObj); } // If we have a parent object, force update the version field if one // exists, and keep firing the notification up the chain. // Otherwise, do nothing. if (parentObj != null) { // Need to check if we are a non cascade locking node within a // cascade locking policy chain. if (realParentDescriptor.usesOptimisticLocking() && realParentDescriptor.getOptimisticLockingPolicy().isCascaded()) { ObjectChangeSet ocs = realParentDescriptor.getObjectBuilder().createObjectChangeSet(parentObj, changeSet, uow); if (!ocs.hasForcedChangesFromCascadeLocking()) { ocs.setHasForcedChangesFromCascadeLocking(true); changeSet.addObjectChangeSet(ocs, uow, true); } } // Keep sending the notification up the chain ... if (realParentDescriptor.hasCascadeLockingPolicies()) { for (CascadeLockingPolicy policy : realParentDescriptor.getCascadeLockingPolicies()) { policy.lockNotifyParent(parentObj, changeSet, uow); } } } } /** * INTERNAL: */ public void setQueryKeyFields(Map queryKeyFields) { setQueryKeyFields(queryKeyFields, true); } /** * INTERNAL: */ public void setQueryKeyFields(Map queryKeyFields, boolean lookForParentMapping) { m_queryKeyFields = queryKeyFields; m_mappedQueryKeyFields = m_queryKeyFields; this.m_lookForParentMapping = lookForParentMapping; } /** * INTERNAL: * Indicates whether to expect unmapped fields. * That should be set to true for UnidirectionalOneToManyMapping. */ public void setShouldHandleUnmappedFields(boolean shouldHandleUnmappedFields) { m_shouldHandleUnmappedFields = shouldHandleUnmappedFields; } /** * INTERNAL: */ public boolean shouldHandleUnmappedFields() { return m_shouldHandleUnmappedFields; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy