/***************************************************************** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. ****************************************************************/ package org.apache.cayenne.access; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.ObjectId; import org.apache.cayenne.map.DbAttribute; import org.apache.cayenne.map.DbJoin; import org.apache.cayenne.map.DbRelationship; import org.apache.cayenne.map.ObjAttribute; import org.apache.cayenne.map.ObjEntity; import org.apache.cayenne.map.ObjRelationship; import org.apache.commons.collections.Transformer; /** * Builds update qualifier snapshots, including optimistic locking. * * @since 1.2 */ class DataNodeSyncQualifierDescriptor { private List<DbAttribute> attributes; private List<Transformer> valueTransformers; private boolean usingOptimisticLocking; public boolean isUsingOptimisticLocking() { return usingOptimisticLocking; } List<DbAttribute> getAttributes() { return attributes; } Map<String, Object> createQualifierSnapshot(ObjectDiff diff) { int len = attributes.size(); Map<String, Object> map = new HashMap<>(len * 2); for (int i = 0; i < len; i++) { DbAttribute attribute = attributes.get(i); if (!map.containsKey(attribute.getName())) { Object value = valueTransformers.get(i).transform(diff); map.put(attribute.getName(), value); } } return map; } void reset(DbEntityClassDescriptor descriptor) { attributes = new ArrayList<>(3); valueTransformers = new ArrayList<>(3); usingOptimisticLocking = descriptor.getEntity().getLockType() == ObjEntity.LOCK_TYPE_OPTIMISTIC; // master PK columns if (descriptor.isMaster()) { for (final DbAttribute attribute : descriptor.getDbEntity().getPrimaryKeys()) { attributes.add(attribute); valueTransformers.add(new Transformer() { public Object transform(Object input) { ObjectId id = (ObjectId) ((ObjectDiff) input).getNodeId(); return id.getIdSnapshot().get(attribute.getName()); } }); } } else { // TODO: andrus 12/23/2007 - only one step relationship is supported... if (descriptor.getPathFromMaster().size() != 1) { throw new CayenneRuntimeException( "Only single step dependent relationships are currently supported. Actual path length: %d" , descriptor.getPathFromMaster().size()); } DbRelationship masterDependentDbRel = descriptor.getPathFromMaster().get(0); if (masterDependentDbRel != null) { for (final DbJoin dbAttrPair : masterDependentDbRel.getJoins()) { DbAttribute dbAttribute = dbAttrPair.getTarget(); if (!attributes.contains(dbAttribute)) { attributes.add(dbAttribute); valueTransformers.add(new Transformer() { public Object transform(Object input) { ObjectId id = (ObjectId) ((ObjectDiff) input).getNodeId(); return id.getIdSnapshot().get(dbAttrPair.getSourceName()); } }); } } } } if (usingOptimisticLocking) { for (final ObjAttribute attribute : descriptor.getEntity().getAttributes()) { if (attribute.isUsedForLocking()) { // only care about first step in a flattened attribute DbAttribute dbAttribute = (DbAttribute) attribute.getDbPathIterator().next(); // only use qualifier if dbEntities match if (dbAttribute.getEntity().equals(descriptor.getDbEntity()) && !attributes.contains(dbAttribute)) { attributes.add(dbAttribute); valueTransformers.add(new Transformer() { public Object transform(Object input) { return ((ObjectDiff) input).getSnapshotValue(attribute.getName()); } }); } } } for (final ObjRelationship relationship : descriptor.getEntity().getRelationships()) { if (relationship.isUsedForLocking()) { // only care about the first DbRelationship DbRelationship dbRelationship = relationship.getDbRelationships().get(0); for (final DbJoin dbAttrPair : dbRelationship.getJoins()) { DbAttribute dbAttribute = dbAttrPair.getSource(); // relationship transformers override attribute transformers for meaningful FK's... // why meaningful FKs can go out of sync is another story (CAY-595) int index = attributes.indexOf(dbAttribute); if (index >= 0 && !dbAttribute.isForeignKey()) { continue; } Transformer transformer = new Transformer() { public Object transform(Object input) { ObjectId targetId = ((ObjectDiff) input).getArcSnapshotValue(relationship.getName()); return targetId != null ? targetId.getIdSnapshot().get(dbAttrPair.getTargetName()) : null; } }; if (index < 0) { attributes.add(dbAttribute); valueTransformers.add(transformer); } else { valueTransformers.set(index, transformer); } } } } } } }