| /* |
| * Copyright (c) 1998, 2020 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.descriptors; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.persistence.exceptions.DatabaseException; |
| import org.eclipse.persistence.internal.helper.DatabaseField; |
| import org.eclipse.persistence.internal.helper.DatabaseTable; |
| import org.eclipse.persistence.internal.sessions.AbstractRecord; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.mappings.DatabaseMapping.WriteType; |
| import org.eclipse.persistence.queries.ObjectLevelModifyQuery; |
| |
| /** |
| * <p> |
| * <b>Purpose</b>: An implementation of the OptimisticLockingPolicy interface. |
| * This policy compares selected fields in the WHERE clause when doing an update |
| * or a delete. If any field has been changed, an optimistic locking exception |
| * will be thrown. Note that the fields specified must be mapped and not be |
| * primary keys. |
| * <p> |
| * NOTE: This policy can only be used inside a unit of work. |
| * |
| * @since TopLink 2.5 |
| */ |
| public class SelectedFieldsLockingPolicy extends FieldsLockingPolicy { |
| protected Map<DatabaseTable, List<DatabaseField>> lockFieldsByTable; |
| protected List<DatabaseField> lockFields; |
| |
| /** |
| * PUBLIC: Create a new selected fields locking policy. A field locking |
| * policy is based on locking on the specified fields by comparing with |
| * their previous values to detect field-level collisions. Note: the unit of |
| * work must be used for all updates when using field locking. |
| */ |
| public SelectedFieldsLockingPolicy() { |
| super(); |
| this.lockFieldsByTable = new HashMap(4); |
| this.lockFields = new ArrayList(); |
| } |
| |
| /** |
| * PUBLIC: Add a field name to lock on. All fields in this list will be |
| * compared when updating if the value of any of the fields does not match |
| * the value in memory, an OptimisticLockException will be thrown. |
| * |
| * Note: An Automatic update will not be done on this field, only a |
| * comparison occurs. |
| */ |
| public void addLockFieldName(String fieldName) { |
| getLockFields().add(new DatabaseField(fieldName)); |
| } |
| |
| /** |
| * INTERNAL: Values to be included in the locking mechanism are added to the |
| * translation row. For changed fields the normal build row is ok as only |
| * changed fields matter. |
| */ |
| @Override |
| public void addLockValuesToTranslationRow(ObjectLevelModifyQuery query) throws DatabaseException { |
| Object object; |
| verifyUsage(query.getSession()); |
| if (query.isDeleteObjectQuery()) { |
| object = query.getObject(); |
| } else { |
| object = query.getBackupClone(); |
| } |
| |
| // EL bug 319759 |
| if (query.isUpdateObjectQuery()) { |
| query.setShouldValidateUpdateCallCacheUse(true); |
| } |
| |
| for (Iterator<List<DatabaseField>> fields = getLockFieldsByTable().values().iterator(); fields.hasNext();) { |
| for (DatabaseField field : fields.next()) { |
| DatabaseMapping mapping = descriptor.getObjectBuilder().getMappingForField(field); |
| // Bug5892889, Exception will be thrown if no matched database |
| // field found |
| if (mapping == null) { |
| throw DatabaseException.specifiedLockingFieldsNotFoundInDatabase(field.getQualifiedName()); |
| } else { |
| mapping.writeFromObjectIntoRow(object, query.getTranslationRow(), query.getSession(), WriteType.UNDEFINED); |
| } |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: returns the lock fields to compare based on the passed in |
| * table. |
| */ |
| @Override |
| protected List<DatabaseField> getFieldsToCompare(org.eclipse.persistence.internal.helper.DatabaseTable table, AbstractRecord transRow, AbstractRecord modifyRow) { |
| return getLockFields(table); |
| } |
| |
| /** |
| * INTERNAL: Returns the lock fields |
| */ |
| public List<DatabaseField> getLockFields() { |
| return lockFields; |
| } |
| |
| /** |
| * INTERNAL: returns the lock fields based on the passed in table |
| */ |
| protected List<DatabaseField> getLockFields(DatabaseTable table) { |
| List<DatabaseField> temp = this.lockFieldsByTable.get(table); |
| if (temp == null) { |
| return Collections.EMPTY_LIST; |
| } |
| return temp; |
| } |
| |
| /** |
| * INTERNAL: returns the lock fields |
| */ |
| protected Map<DatabaseTable, List<DatabaseField>> getLockFieldsByTable() { |
| return lockFieldsByTable; |
| } |
| |
| /** |
| * INTERNAL: It is responsible for initializing the policy; |
| */ |
| @Override |
| public void initialize(AbstractSession session) { |
| super.initialize(session); |
| List<DatabaseField> lockFields = getLockFields(); |
| int size = lockFields.size(); |
| for (int index = 0; index < size; index++) { |
| DatabaseField field = lockFields.get(index); |
| field = descriptor.buildField(field); |
| lockFields.set(index, field); |
| List<DatabaseField> fieldsForTable = getLockFieldsByTable().get(field.getTable()); |
| if (fieldsForTable == null) { |
| fieldsForTable = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(); |
| getLockFieldsByTable().put(field.getTable(), fieldsForTable); |
| } |
| fieldsForTable.add(field); |
| } |
| } |
| |
| /** |
| * PUBLIC: Set the field names to lock on. All fields in this list will be |
| * compared when Updating. If the value of any of the fields does not match |
| * the value in memory, an OptimisticLockException will be thrown. |
| * |
| * Note: An Automatic update will not be done on this field, only a |
| * comparison occurs. |
| */ |
| public void setLockFieldNames(List<String> lockFieldNames) { |
| for (String name : lockFieldNames) { |
| addLockFieldName(name); |
| } |
| } |
| |
| /** |
| * INTERNAL: Sets the lock fields |
| */ |
| protected void setLockFields(List<DatabaseField> lockFields) { |
| this.lockFields = lockFields; |
| } |
| |
| /** |
| * INTERNAL: Used to set the field names to be used in this policy. |
| */ |
| protected void setLockFieldsByTable(Map<DatabaseTable, List<DatabaseField>> lockFieldsByTable) { |
| this.lockFieldsByTable = lockFieldsByTable; |
| } |
| } |