org.eclipse.persistence.internal.descriptors.CascadeLockingPolicy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 1998, 2019 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 java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Iterator;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.InheritancePolicy;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.internal.expressions.SQLSelectStatement;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.queries.ReadObjectQuery;
import org.eclipse.persistence.queries.DataReadQuery;
import org.eclipse.persistence.sessions.DatabaseRecord;
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;
/**
* 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 = ((DatabaseField) 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
* @param parentObj
* @return
*/
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;
}
}