PageRenderTime 31ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

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