/src/com/google/appengine/datanucleus/mapping/DependentDeleteRequest.java
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}