PageRenderTime 52ms CodeModel.GetById 13ms app.highlight 36ms RepoModel.GetById 1ms app.codeStats 0ms

/src/com/google/appengine/datanucleus/mapping/DependentDeleteRequest.java

http://datanucleus-appengine.googlecode.com/
Java | 263 lines | 178 code | 28 blank | 57 comment | 60 complexity | 972ca825b8320e0158efb294be3ce4c9 MD5 | raw file
  1/**********************************************************************
  2Copyright (c) 2009 Google Inc.
  3
  4Licensed under the Apache License, Version 2.0 (the "License");
  5you may not use this file except in compliance with the License.
  6You may obtain a copy of the License at
  7
  8http://www.apache.org/licenses/LICENSE-2.0
  9
 10Unless required by applicable law or agreed to in writing, software
 11distributed under the License is distributed on an "AS IS" BASIS,
 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13See the License for the specific language governing permissions and
 14limitations under the License.
 15**********************************************************************/
 16package com.google.appengine.datanucleus.mapping;
 17
 18import com.google.appengine.api.datastore.Entity;
 19import com.google.appengine.datanucleus.DatastoreManager;
 20import com.google.appengine.datanucleus.MetaDataUtils;
 21import com.google.appengine.datanucleus.Utils;
 22
 23import org.datanucleus.ClassLoaderResolver;
 24import org.datanucleus.metadata.AbstractClassMetaData;
 25import org.datanucleus.metadata.AbstractMemberMetaData;
 26import org.datanucleus.metadata.RelationType;
 27import org.datanucleus.ExecutionContext;
 28import org.datanucleus.state.ObjectProvider;
 29import org.datanucleus.store.mapped.DatastoreClass;
 30import org.datanucleus.store.mapped.DatastoreField;
 31import org.datanucleus.store.mapped.MappedStoreManager;
 32import org.datanucleus.store.mapped.mapping.ArrayMapping;
 33import org.datanucleus.store.mapped.mapping.JavaTypeMapping;
 34import org.datanucleus.store.mapped.mapping.MapMapping;
 35import org.datanucleus.store.mapped.mapping.MappingCallbacks;
 36import org.datanucleus.store.mapped.mapping.MappingConsumer;
 37import org.datanucleus.store.mapped.mapping.PersistableMapping;
 38import org.datanucleus.store.mapped.mapping.ReferenceMapping;
 39
 40import java.lang.reflect.Array;
 41import java.util.HashSet;
 42import java.util.Iterator;
 43import java.util.List;
 44import java.util.Map;
 45import java.util.Set;
 46
 47/**
 48 * Encapsulates logic that supports deletion of dependent objects.
 49 * Code based pretty closely on the rdbms version of DeleteRequest.
 50 *
 51 * @author Max Ross <maxr@google.com>
 52 */
 53public class DependentDeleteRequest {
 54
 55  private final List<MappingCallbacks> callbacks;
 56
 57  /**
 58   * 1-1 bidir non-owner fields that are reachable (but not updated) and have no datastore column.
 59   */
 60  private final AbstractMemberMetaData[] oneToOneNonOwnerFields;
 61
 62  public DependentDeleteRequest(DatastoreClass dc, AbstractClassMetaData acmd, ClassLoaderResolver clr) {
 63    DependentDeleteMappingConsumer consumer = new DependentDeleteMappingConsumer(acmd, clr);
 64    dc.provideNonPrimaryKeyMappings(consumer); // to compute callbacks
 65    dc.providePrimaryKeyMappings(consumer);
 66    dc.provideDatastoreIdMappings(consumer);
 67    callbacks = consumer.getMappingCallBacks();
 68    oneToOneNonOwnerFields = consumer.getOneToOneNonOwnerFields();
 69  }
 70
 71  /**
 72   * Perform the work of the delete request. Calls preDelete on related fields, and nulls other
 73   * relations as required to allow the deletion of the owner. 
 74   * @return Dependent objects that need deleting after the delete of the owner
 75   */
 76  public Set execute(ObjectProvider op, Entity owningEntity) {
 77
 78    Set relatedObjectsToDelete = null;
 79
 80    // Process all related fields first
 81    // a). Delete any dependent objects
 82    // b). Null any non-dependent objects with FK at other side
 83    ClassLoaderResolver clr = op.getExecutionContext().getClassLoaderResolver();
 84    DatastoreManager storeMgr = (DatastoreManager)op.getExecutionContext().getStoreManager();
 85    for (MappingCallbacks callback : callbacks) {
 86      JavaTypeMapping mapping = (JavaTypeMapping) callback;
 87      AbstractMemberMetaData mmd = mapping.getMemberMetaData();
 88      RelationType relationType = mmd.getRelationType(clr);
 89      if (callback instanceof ArrayMapping) {
 90        // Handle dependent field delete
 91        if (RelationType.isRelationMultiValued(relationType)) {
 92          // Field of type PC[], make sure it is loaded and handle dependent-element
 93          ExecutionContext ec = op.getExecutionContext();
 94          op.loadField(mmd.getAbsoluteFieldNumber());
 95          Object arr = op.provideField(mmd.getAbsoluteFieldNumber());
 96          if (mmd.getArray().isDependentElement() || MetaDataUtils.isOwnedRelation(mmd, storeMgr)) {
 97            if (arr != null) {
 98              for (int i=0;i<Array.getLength(arr); i++) {
 99                Object elem = Array.get(arr, i);
100                if (ec.getApiAdapter().isPersistent(elem)) {
101                  ec.deleteObjectInternal(elem);
102                }
103              }
104            }
105          }
106        }
107      } else if (callback instanceof MapMapping) {
108        if (RelationType.isRelationMultiValued(relationType)) {
109          // Field of type Map<PC,PC> or Map<NonPC,PC> or Map<PC,NonPC>, make sure it is loaded and handle dependent-key/value
110          ExecutionContext ec = op.getExecutionContext();
111          op.loadField(mmd.getAbsoluteFieldNumber());
112          Map map = (Map)op.provideField(mmd.getAbsoluteFieldNumber());
113          if (map != null) {
114            if (mmd.getMap().isDependentKey() || MetaDataUtils.isOwnedRelation(mmd, storeMgr)) {
115              Iterator keyIter = map.keySet().iterator();
116              while (keyIter.hasNext()) {
117                Object key = keyIter.next();
118                if (ec.getApiAdapter().isPersistent(key)) {
119                  ec.deleteObjectInternal(key);
120                }
121              }
122            }
123            if (mmd.getMap().isDependentValue() || MetaDataUtils.isOwnedRelation(mmd, storeMgr)) {
124              Iterator valIter = map.values().iterator();
125              while (valIter.hasNext()) {
126                Object val = valIter.next();
127                if (ec.getApiAdapter().isPersistent(val)) {
128                  ec.deleteObjectInternal(val);
129                }
130              }
131            }
132          }
133        }
134      } else {
135        // Perform delete-dependent using backing store
136        callback.preDelete(op);
137      }
138
139      if (mmd.isDependent() && (relationType == RelationType.ONE_TO_ONE_UNI ||
140          (relationType == RelationType.ONE_TO_ONE_BI && mmd.getMappedBy() == null))) {
141        // Make sure the field is loaded
142        op.loadField(mmd.getAbsoluteFieldNumber());
143        Object relatedPc = op.provideField(mmd.getAbsoluteFieldNumber());
144        if (relatedPc != null) {
145          boolean relatedObjectDeleted = op.getExecutionContext().getApiAdapter().isDeleted(relatedPc);
146          if (!relatedObjectDeleted) {
147            if (relatedObjectsToDelete == null) {
148              relatedObjectsToDelete = new HashSet();
149            }
150            relatedObjectsToDelete.add(relatedPc);
151          }
152        }
153      }
154    }
155
156    if (oneToOneNonOwnerFields != null && oneToOneNonOwnerFields.length > 0) {
157      for (AbstractMemberMetaData relatedFmd : oneToOneNonOwnerFields) {
158        updateOneToOneBidirectionalOwnerObjectForField(op, relatedFmd, owningEntity);
159      }
160    }
161
162    return relatedObjectsToDelete;
163  }
164
165  private void updateOneToOneBidirectionalOwnerObjectForField(
166      ObjectProvider op, AbstractMemberMetaData fmd, Entity owningEntity) {
167    MappedStoreManager storeMgr = (MappedStoreManager) op.getExecutionContext().getStoreManager();
168    ExecutionContext ec = op.getExecutionContext();
169    ClassLoaderResolver clr = ec.getClassLoaderResolver();
170    AbstractMemberMetaData[] relatedMmds = fmd.getRelatedMemberMetaData(clr);
171    String fullClassName = ((AbstractClassMetaData) relatedMmds[0].getParent()).getFullClassName();
172    DatastoreClass refTable = storeMgr.getDatastoreClass(fullClassName, clr);
173    JavaTypeMapping refMapping = refTable.getMemberMapping(fmd.getMappedBy());
174    if (refMapping.isNullable()) {
175      // Null out the relationship to the object being deleted.
176      refMapping.setObject(ec, owningEntity, new int[1], op.getObject());
177
178      // TODO If the object being deleted is this objects parent, do we delete this?
179      // TODO(maxr): Do I need to manually request an update now?
180    }
181  }
182
183  /**
184   * Mapping consumer for dependent deletes.  Largely based on rdbms
185   * DeleteRequest.DeleteMappingConsumer
186   */
187  private static class DependentDeleteMappingConsumer implements MappingConsumer {
188    /** Fields in a 1-1 relation with FK in the table of the other object. */
189    private final List<AbstractMemberMetaData> oneToOneNonOwnerFields = Utils.newArrayList();
190
191    private final List<MappingCallbacks> callbacks = Utils.newArrayList();
192
193    private final ClassLoaderResolver clr;
194
195    private final AbstractClassMetaData cmd;
196    
197    public DependentDeleteMappingConsumer(AbstractClassMetaData cmd, ClassLoaderResolver clr) {
198      this.clr = clr;
199      this.cmd = cmd;
200    }
201
202    public void preConsumeMapping(int highest) {
203    }
204
205    public void consumeMapping(JavaTypeMapping m, AbstractMemberMetaData fmd) {
206      if (!fmd.getAbstractClassMetaData().isSameOrAncestorOf(cmd)) {
207        // Make sure we only accept mappings from the correct part of any inheritance tree
208        return;
209      }
210
211      if (m.includeInUpdateStatement()) {
212        if (fmd.isPrimaryKey()) {
213        } else if (m instanceof PersistableMapping || m instanceof ReferenceMapping) {
214          if (m.getNumberOfDatastoreMappings() == 0) {
215            // Field storing a PC object with FK at other side
216            RelationType relationType = fmd.getRelationType(clr);
217            if (relationType == RelationType.ONE_TO_ONE_BI) {
218              if (fmd.getMappedBy() != null) {
219                // 1-1 bidirectional field without datastore column(s) (with single FK at other side)
220                oneToOneNonOwnerFields.add(fmd);
221              }
222            } else if (relationType == RelationType.MANY_TO_ONE_BI) {
223              AbstractMemberMetaData[] relatedMmds = fmd.getRelatedMemberMetaData(clr);
224              if (fmd.getJoinMetaData() != null || relatedMmds[0].getJoinMetaData() != null) {
225                // 1-N bidirectional using join table for relation
226                // TODO Anything to do here ?
227              }
228            }
229          }
230        }
231      }
232
233      // Build up list of mappings callbacks for the fields of this class.
234      // The Mapping callback called delete is the preDelete
235      if (m instanceof MappingCallbacks) {
236        callbacks.add((MappingCallbacks) m);
237      }
238    }
239
240    /**
241     * All 1-1 bidirectional non-owner fields, with the FK In the other object.
242     *
243     * @return The fields that are 1-1 bidirectional with the FK at the other side.
244     */
245    public AbstractMemberMetaData[] getOneToOneNonOwnerFields() {
246      AbstractMemberMetaData[] fmds = new AbstractMemberMetaData[oneToOneNonOwnerFields.size()];
247      for (int i = 0; i < oneToOneNonOwnerFields.size(); ++i) {
248        fmds[i] = oneToOneNonOwnerFields.get(i);
249      }
250      return fmds;
251    }
252
253    public List<MappingCallbacks> getMappingCallBacks() {
254      return callbacks;
255    }
256
257    public void consumeUnmappedDatastoreField(DatastoreField fld) {
258    }
259
260    public void consumeMapping(JavaTypeMapping m, int mappingType) {
261    }
262  }
263}