/src/com/google/appengine/datanucleus/scostore/FKListStore.java

http://datanucleus-appengine.googlecode.com/ · Java · 1119 lines · 798 code · 98 blank · 223 comment · 245 complexity · 6e82f5f100d6c05b9f7a6d8f67942268 MD5 · raw file

  1. /**********************************************************************
  2. Copyright (c) 2011 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.scostore;
  14. import java.util.ArrayList;
  15. import java.util.Collection;
  16. import java.util.Collections;
  17. import java.util.Iterator;
  18. import java.util.List;
  19. import java.util.ListIterator;
  20. import java.util.Set;
  21. import org.datanucleus.ClassLoaderResolver;
  22. import org.datanucleus.ClassNameConstants;
  23. import org.datanucleus.FetchPlan;
  24. import org.datanucleus.api.ApiAdapter;
  25. import org.datanucleus.exceptions.NucleusDataStoreException;
  26. import org.datanucleus.exceptions.NucleusFatalUserException;
  27. import org.datanucleus.exceptions.NucleusUserException;
  28. import org.datanucleus.metadata.AbstractClassMetaData;
  29. import org.datanucleus.metadata.AbstractMemberMetaData;
  30. import org.datanucleus.metadata.OrderMetaData;
  31. import org.datanucleus.metadata.RelationType;
  32. import org.datanucleus.ExecutionContext;
  33. import org.datanucleus.store.FieldValues;
  34. import org.datanucleus.state.ObjectProvider;
  35. import org.datanucleus.store.mapped.DatastoreClass;
  36. import org.datanucleus.store.mapped.exceptions.MappedDatastoreException;
  37. import org.datanucleus.store.mapped.mapping.JavaTypeMapping;
  38. import org.datanucleus.store.mapped.mapping.MappingConsumer;
  39. import org.datanucleus.store.scostore.ListStore;
  40. import org.datanucleus.util.NucleusLogger;
  41. import org.datanucleus.util.StringUtils;
  42. import com.google.appengine.api.datastore.DatastoreService;
  43. import com.google.appengine.api.datastore.DatastoreServiceConfig;
  44. import com.google.appengine.api.datastore.Entity;
  45. import com.google.appengine.api.datastore.EntityNotFoundException;
  46. import com.google.appengine.api.datastore.Key;
  47. import com.google.appengine.api.datastore.KeyFactory;
  48. import com.google.appengine.api.datastore.Query;
  49. import com.google.appengine.datanucleus.DatastoreManager;
  50. import com.google.appengine.datanucleus.DatastoreServiceFactoryInternal;
  51. import com.google.appengine.datanucleus.EntityUtils;
  52. import com.google.appengine.datanucleus.KeyRegistry;
  53. import com.google.appengine.datanucleus.MetaDataUtils;
  54. import com.google.appengine.datanucleus.StorageVersion;
  55. import com.google.appengine.datanucleus.Utils;
  56. import com.google.appengine.datanucleus.query.LazyResult;
  57. /**
  58. * Backing store for lists stored with a "FK" in the element.
  59. */
  60. public class FKListStore extends AbstractFKStore implements ListStore {
  61. /** Mapping for the ordering column in the element table. */
  62. protected JavaTypeMapping orderMapping;
  63. /** Whether the list is indexed (like with JDO). If false then it will have no orderMapping (like with JPA). */
  64. protected boolean indexedList = true;
  65. private final ThreadLocal<Boolean> removing = new ThreadLocal<Boolean>() {
  66. @Override
  67. protected Boolean initialValue() {
  68. return false;
  69. }
  70. };
  71. public FKListStore(AbstractMemberMetaData ownerMmd, DatastoreManager storeMgr, ClassLoaderResolver clr) {
  72. super(ownerMmd, storeMgr, clr);
  73. orderMapping = elementTable.getExternalMapping(ownerMemberMetaData, MappingConsumer.MAPPING_TYPE_EXTERNAL_INDEX);
  74. if (ownerMemberMetaData.getOrderMetaData() != null && !ownerMemberMetaData.getOrderMetaData().isIndexedList()) {
  75. indexedList = false;
  76. }
  77. if (!storeMgr.storageVersionAtLeast(StorageVersion.READ_OWNED_CHILD_KEYS_FROM_PARENTS) &&
  78. orderMapping == null && indexedList) {
  79. // Early storage version requires that indexedList has an order mapping in the element
  80. throw new NucleusUserException(LOCALISER.msg("056041",
  81. ownerMemberMetaData.getAbstractClassMetaData().getFullClassName(), ownerMemberMetaData.getName(), elementType));
  82. }
  83. }
  84. /* (non-Javadoc)
  85. * @see org.datanucleus.store.scostore.CollectionStore#hasOrderMapping()
  86. */
  87. public boolean hasOrderMapping() {
  88. return (orderMapping != null);
  89. }
  90. /* (non-Javadoc)
  91. * @see org.datanucleus.store.scostore.CollectionStore#add(org.datanucleus.store.ObjectProvider, java.lang.Object, int)
  92. */
  93. public boolean add(ObjectProvider ownerOP, Object element, int currentSize) {
  94. return internalAdd(ownerOP, 0, true, Collections.singleton(element), currentSize);
  95. }
  96. /* (non-Javadoc)
  97. * @see org.datanucleus.store.scostore.CollectionStore#addAll(org.datanucleus.store.ObjectProvider, java.util.Collection, int)
  98. */
  99. public boolean addAll(ObjectProvider ownerOP, Collection elements, int currentSize) {
  100. return internalAdd(ownerOP, 0, true, elements, currentSize);
  101. }
  102. /* (non-Javadoc)
  103. * @see org.datanucleus.store.scostore.ListStore#add(org.datanucleus.store.ObjectProvider, java.lang.Object, int, int)
  104. */
  105. public void add(ObjectProvider ownerOP, Object element, int index, int currentSize) {
  106. internalAdd(ownerOP, index, false, Collections.singleton(element), currentSize);
  107. }
  108. /* (non-Javadoc)
  109. * @see org.datanucleus.store.scostore.ListStore#addAll(org.datanucleus.store.ObjectProvider, java.util.Collection, int, int)
  110. */
  111. public boolean addAll(ObjectProvider ownerOP, Collection elements, int index, int currentSize) {
  112. return internalAdd(ownerOP, index, false, elements, currentSize);
  113. }
  114. /**
  115. * Internal method for adding an item to the List.
  116. * @param ownerOP Object Provider of the owner of the list
  117. * @param startAt The start position
  118. * @param atEnd Whether to add at the end
  119. * @param elements The Collection of elements to add.
  120. * @param currentSize Current size of List (if known). -1 if not known
  121. * @return Whether it was successful
  122. */
  123. protected boolean internalAdd(ObjectProvider ownerOP, int startAt, boolean atEnd, Collection elements, int currentSize) {
  124. boolean success = false;
  125. if (elements == null || elements.size() == 0) {
  126. success = true;
  127. }
  128. else {
  129. if (!storeMgr.storageVersionAtLeast(StorageVersion.WRITE_OWNED_CHILD_KEYS_TO_PARENTS)) {
  130. // Check what we have persistent already
  131. int currentListSize = 0;
  132. if (currentSize < 0) {
  133. // Get the current size from the datastore
  134. currentListSize = size(ownerOP);
  135. }
  136. else {
  137. currentListSize = currentSize;
  138. }
  139. boolean shiftingElements = true;
  140. if (atEnd || startAt == currentListSize) {
  141. shiftingElements = false;
  142. startAt = currentListSize; // Not shifting so we insert from the end
  143. }
  144. if (shiftingElements)
  145. {
  146. // We need to shift existing elements before positioning the new ones
  147. try {
  148. // Calculate the amount we need to shift any existing elements by
  149. // This is used where inserting between existing elements and have to shift down all elements after the start point
  150. int shift = elements.size();
  151. // shift up existing elements after start position by "shift"
  152. for (int i=currentListSize-1; i>=startAt; i--) {
  153. internalShift(ownerOP, false, i, shift);
  154. }
  155. }
  156. catch (MappedDatastoreException e) {
  157. // An error was encountered during the shift process so abort here
  158. throw new NucleusDataStoreException(LOCALISER.msg("056009", e.getMessage()), e.getCause());
  159. }
  160. }
  161. }
  162. boolean elementsNeedPositioning = false;
  163. int position = startAt;
  164. Iterator elementIter = elements.iterator();
  165. while (elementIter.hasNext()) {
  166. Object element = elementIter.next();
  167. if (MetaDataUtils.isOwnedRelation(ownerMemberMetaData, storeMgr)) {
  168. // Register the parent key for the element when owned
  169. Key parentKey = EntityUtils.getKeyForObject(ownerOP.getObject(), ownerOP.getExecutionContext());
  170. KeyRegistry.getKeyRegistry(ownerOP.getExecutionContext()).registerParentKeyForOwnedObject(element, parentKey);
  171. }
  172. // Persist any non-persistent objects at their final list position (persistence-by-reachability)
  173. boolean inserted = validateElementForWriting(ownerOP, element, position);
  174. if (!inserted) {
  175. if (!storeMgr.storageVersionAtLeast(StorageVersion.WRITE_OWNED_CHILD_KEYS_TO_PARENTS)) {
  176. // This element wasn't positioned in the validate so we need to set the positions later
  177. elementsNeedPositioning = true;
  178. }
  179. }
  180. position++;
  181. }
  182. if (elementsNeedPositioning) {
  183. // Some elements have been shifted so the new elements need positioning now, or we already had some
  184. // of the new elements persistent and so they need their positions setting now
  185. elementIter = elements.iterator();
  186. while (elementIter.hasNext()) {
  187. Object element = elementIter.next();
  188. updateElementFk(ownerOP, element, ownerOP.getObject(), startAt);
  189. startAt++;
  190. }
  191. }
  192. success = true;
  193. }
  194. return success;
  195. }
  196. protected int[] internalShift(ObjectProvider op, boolean batched, int oldIndex, int amount)
  197. throws MappedDatastoreException {
  198. if (orderMapping == null) {
  199. return null;
  200. }
  201. DatastoreServiceConfig config = storeMgr.getDefaultDatastoreServiceConfigForReads();
  202. DatastoreService service = DatastoreServiceFactoryInternal.getDatastoreService(config);
  203. AbstractClassMetaData acmd = elementCmd;
  204. String kind =
  205. storeMgr.getIdentifierFactory().newDatastoreContainerIdentifier(acmd).getIdentifierName();
  206. Query q = new Query(kind);
  207. ExecutionContext ec = op.getExecutionContext();
  208. Object id = ec.getApiAdapter().getTargetKeyForSingleFieldIdentity(op.getInternalObjectId());
  209. Key key = id instanceof Key ? (Key) id : KeyFactory.stringToKey((String) id);
  210. q.setAncestor(key);
  211. // create an entity just to capture the name of the index property
  212. Entity entity = new Entity(kind);
  213. orderMapping.setObject(ec, entity, new int[] {1}, oldIndex);
  214. String indexProp = entity.getProperties().keySet().iterator().next();
  215. q.addFilter(indexProp, Query.FilterOperator.GREATER_THAN_OR_EQUAL, oldIndex);
  216. for (Entity shiftMe : service.prepare(service.getCurrentTransaction(null), q).asIterable()) {
  217. Long pos = (Long) shiftMe.getProperty(indexProp);
  218. shiftMe.setProperty(indexProp, pos + amount);
  219. EntityUtils.putEntityIntoDatastore(ec, shiftMe);
  220. }
  221. return null;
  222. }
  223. /**
  224. * Utility to update a foreign-key in the element in the case of a unidirectional 1-N relationship.
  225. * @param op ObjectProvider for the owner
  226. * @param element The element to update
  227. * @param owner The owner object to set in the FK
  228. * @param index The index position (or -1 if not known)
  229. * @return Whether it was performed successfully
  230. */
  231. protected boolean updateElementFk(ObjectProvider op, Object element, Object owner, int index) {
  232. if (element == null) {
  233. return false;
  234. }
  235. // Keys (and therefore parents) are immutable so we don't need to ever
  236. // actually update the parent FK, but we do need to check to make sure
  237. // someone isn't trying to modify the parent FK
  238. if (MetaDataUtils.isOwnedRelation(ownerMemberMetaData, storeMgr)) {
  239. EntityUtils.checkParentage(element, op);
  240. }
  241. if (orderMapping == null) {
  242. return false;
  243. }
  244. return true;
  245. }
  246. /**
  247. * Convenience method for whether we should delete elements when clear()/remove() is called.
  248. * @return Whether to delete an element on call of clear()/remove()
  249. */
  250. protected boolean deleteElementsOnRemoveOrClear() {
  251. boolean deleteElements = false;
  252. boolean dependent = ownerMemberMetaData.getCollection().isDependentElement();
  253. if (ownerMemberMetaData.isCascadeRemoveOrphans()) {
  254. dependent = true;
  255. }
  256. if (dependent) {
  257. // Elements are dependent and can't exist on their own, so delete them all
  258. NucleusLogger.DATASTORE.debug(LOCALISER.msg("056034"));
  259. deleteElements = true;
  260. } else {
  261. if ((ownerMapping.isNullable() && orderMapping == null) ||
  262. (ownerMapping.isNullable() && orderMapping != null && orderMapping.isNullable())) {
  263. // Field isn't dependent, and is nullable, so we'll null it
  264. NucleusLogger.DATASTORE.debug(LOCALISER.msg("056036"));
  265. deleteElements = false;
  266. } else {
  267. if (MetaDataUtils.isOwnedRelation(ownerMemberMetaData, storeMgr)) {
  268. // Field is not dependent, and not nullable so we just delete the elements
  269. NucleusLogger.DATASTORE.debug(LOCALISER.msg("056035"));
  270. deleteElements = true;
  271. } else {
  272. // Unowned relation doesn't care since FK is not stored
  273. }
  274. }
  275. }
  276. return deleteElements;
  277. }
  278. /* (non-Javadoc)
  279. * @see org.datanucleus.store.scostore.CollectionStore#clear(org.datanucleus.store.ObjectProvider)
  280. */
  281. public void clear(ObjectProvider op) {
  282. boolean deleteElements = deleteElementsOnRemoveOrClear();
  283. ExecutionContext ec = op.getExecutionContext();
  284. Iterator elementsIter = iterator(op);
  285. if (elementsIter != null) {
  286. while (elementsIter.hasNext()) {
  287. Object element = elementsIter.next();
  288. if (ec.getApiAdapter().isPersistable(element) && ec.getApiAdapter().isDeleted(element)) {
  289. // Element is waiting to be deleted so flush it (it has the FK)
  290. ObjectProvider objSM = ec.findObjectProvider(element);
  291. objSM.flush();
  292. } else {
  293. if (deleteElements) {
  294. ec.deleteObjectInternal(element);
  295. }
  296. }
  297. }
  298. }
  299. }
  300. /* (non-Javadoc)
  301. * @see org.datanucleus.store.scostore.CollectionStore#iterator(org.datanucleus.store.ObjectProvider)
  302. */
  303. public Iterator iterator(ObjectProvider op) {
  304. return listIterator(op);
  305. }
  306. /* (non-Javadoc)
  307. * @see org.datanucleus.store.scostore.ListStore#listIterator(org.datanucleus.store.ObjectProvider)
  308. */
  309. public ListIterator listIterator(ObjectProvider op) {
  310. return listIterator(op, -1, -1);
  311. }
  312. protected ListIterator listIterator(ObjectProvider op, int startIdx, int endIdx) {
  313. ExecutionContext ec = op.getExecutionContext();
  314. if (MetaDataUtils.readRelatedKeysFromParent(storeMgr, ownerMemberMetaData)) {
  315. // Get child keys from property in owner Entity if the property exists
  316. Entity datastoreEntity = getOwnerEntity(op);
  317. String propName = EntityUtils.getPropertyName(storeMgr.getIdentifierFactory(), ownerMemberMetaData);
  318. if (datastoreEntity.hasProperty(propName)) {
  319. if (indexedList) {
  320. return getChildrenFromParentField(op, ec, startIdx, endIdx).listIterator();
  321. } else if (!MetaDataUtils.isOwnedRelation(ownerMemberMetaData, storeMgr)) {
  322. Object value = datastoreEntity.getProperty(propName);
  323. if (value == null || (value instanceof Collection && ((Collection)value).isEmpty())) {
  324. // No elements so just return
  325. return Utils.newArrayList().listIterator();
  326. }
  327. return getChildrenByKeys((List<Key>) value, ec); // TODO Use startIdx,endIdx
  328. } else {
  329. // TODO Get the objects and then order them in-memory using the order criteria
  330. }
  331. } else {
  332. if (op.getLifecycleState().isDeleted()) {
  333. // Object has been deleted so just return empty list
  334. return Utils.newArrayList().listIterator();
  335. }
  336. }
  337. }
  338. if (MetaDataUtils.isOwnedRelation(ownerMemberMetaData, storeMgr)) {
  339. // Get child keys by doing a query with the owner as the parent Entity
  340. Key parentKey = EntityUtils.getPrimaryKeyAsKey(ec.getApiAdapter(), op);
  341. return getChildrenUsingParentQuery(parentKey, getFilterPredicates(startIdx, endIdx), getSortPredicates(), ec).listIterator();
  342. } else {
  343. return Utils.newArrayList().listIterator();
  344. }
  345. }
  346. ListIterator<?> getChildrenByKeys(List<Key> childKeys, final ExecutionContext ec) {
  347. String kindName = elementTable.getIdentifier().getIdentifierName();
  348. Query q = new Query(kindName);
  349. NucleusLogger.PERSISTENCE.debug("Preparing to query for " + childKeys);
  350. q.addFilter(Entity.KEY_RESERVED_PROPERTY, Query.FilterOperator.IN, childKeys);
  351. for (Query.SortPredicate sp : getSortPredicates()) {
  352. q.addSort(sp.getPropertyName(), sp.getDirection());
  353. }
  354. DatastoreServiceConfig config = storeMgr.getDefaultDatastoreServiceConfigForReads();
  355. DatastoreService ds = DatastoreServiceFactoryInternal.getDatastoreService(config);
  356. Utils.Function<Entity, Object> func = new Utils.Function<Entity, java.lang.Object>() {
  357. @Override
  358. public Object apply(Entity from) {
  359. return EntityUtils.entityToPojo(from, elementCmd, clr, ec, false, ec.getFetchPlan());
  360. }
  361. };
  362. return new LazyResult(ds.prepare(q).asIterable(), func, true).listIterator();
  363. }
  364. @Override
  365. public int size(ObjectProvider op) {
  366. if (storeMgr.storageVersionAtLeast(StorageVersion.READ_OWNED_CHILD_KEYS_FROM_PARENTS) && !indexedList) {
  367. if (MetaDataUtils.isOwnedRelation(ownerMemberMetaData, storeMgr)) {
  368. // Ordered list can only be done via parent key currently
  369. return getSizeUsingParentKeyInChildren(op);
  370. } else {
  371. throw new NucleusFatalUserException("Dont currently support ordered lists that are unowned");
  372. }
  373. }
  374. return super.size(op);
  375. }
  376. /* (non-Javadoc)
  377. * @see org.datanucleus.store.scostore.CollectionStore#remove(org.datanucleus.store.ObjectProvider, java.lang.Object, int, boolean)
  378. */
  379. public boolean remove(ObjectProvider op, Object element, int currentSize, boolean allowCascadeDelete) {
  380. ExecutionContext ec = op.getExecutionContext();
  381. if (!validateElementForReading(ec, element)) {
  382. return false;
  383. }
  384. Object elementToRemove = element;
  385. if (ec.getApiAdapter().isDetached(element)) {
  386. // Element passed in is detached so find attached version (DON'T attach this object)
  387. elementToRemove = ec.findObject(ec.getApiAdapter().getIdForObject(element), true, false,
  388. element.getClass().getName());
  389. }
  390. return internalRemove(op, elementToRemove, currentSize);
  391. }
  392. /* (non-Javadoc)
  393. * @see org.datanucleus.store.scostore.CollectionStore#removeAll(org.datanucleus.store.ObjectProvider, java.util.Collection, int)
  394. */
  395. public boolean removeAll(ObjectProvider ownerOP, Collection elements, int currentSize) {
  396. if (elements == null || elements.size() == 0) {
  397. return false;
  398. }
  399. boolean modified = false;
  400. if (indexedList) {
  401. // Get the indices of the elements to remove in reverse order (highest first)
  402. int[] indices = getIndicesOf(ownerOP, elements);
  403. // Remove each element in turn, doing the shifting of indexes each time
  404. // TODO : Change this to remove all in one go and then shift once
  405. for (int i=0;i<indices.length;i++) {
  406. removeAt(ownerOP, indices[i], -1);
  407. modified = true;
  408. }
  409. }
  410. else {
  411. // Ordered List, so remove the elements
  412. Iterator iter = elements.iterator();
  413. while (iter.hasNext()) {
  414. Object element = iter.next();
  415. boolean mod = internalRemove(ownerOP, element, -1);
  416. if (mod) {
  417. modified = true;
  418. }
  419. }
  420. }
  421. return modified;
  422. }
  423. /* (non-Javadoc)
  424. * @see org.datanucleus.store.scostore.ListStore#remove(org.datanucleus.store.ObjectProvider, int, int)
  425. */
  426. public Object remove(ObjectProvider ownerOP, int index, int currentSize) {
  427. Object element = get(ownerOP, index);
  428. if (indexedList) {
  429. // Remove the element at this position
  430. removeAt(ownerOP, index, currentSize);
  431. }
  432. else {
  433. // Ordered list doesn't allow indexed removal so just remove the element
  434. internalRemove(ownerOP, element, currentSize);
  435. }
  436. // TODO This does delete of element, yet internalRemove/removeAt also do
  437. boolean dependent = ownerMemberMetaData.getCollection().isDependentElement();
  438. if (ownerMemberMetaData.isCascadeRemoveOrphans()) {
  439. dependent = true;
  440. }
  441. if (dependent && !ownerMemberMetaData.getCollection().isEmbeddedElement()) {
  442. if (!contains(ownerOP, element)) {
  443. // Delete the element if it is dependent and doesn't have a duplicate entry in the list
  444. ownerOP.getExecutionContext().deleteObjectInternal(element);
  445. }
  446. }
  447. return element;
  448. }
  449. /**
  450. * Convenience method to remove the specified element from the List.
  451. * @param ownerOP ObjectProvider of the owner
  452. * @param element The element
  453. * @return Whether the List was modified
  454. */
  455. protected boolean internalRemove(ObjectProvider ownerOP, Object element, int size)
  456. {
  457. if (indexedList) {
  458. // Indexed List - The element can be at one position only (no duplicates allowed in FK list)
  459. int index = indexOf(ownerOP, element);
  460. if (index == -1) {
  461. return false;
  462. }
  463. removeAt(ownerOP, index, size);
  464. }
  465. else {
  466. // Ordered List - no index so null the FK (if nullable) or delete the element
  467. ExecutionContext ec = ownerOP.getExecutionContext();
  468. if (ownerMapping.isNullable()) {
  469. // Nullify the FK
  470. ObjectProvider elementSM = ec.findObjectProvider(element);
  471. if (relationType == RelationType.ONE_TO_MANY_BI) {
  472. // TODO This is ManagedRelations - move into RelationshipManager
  473. elementSM.replaceFieldMakeDirty(ownerMemberMetaData.getRelatedMemberMetaData(clr)[0].getAbsoluteFieldNumber(),
  474. null);
  475. if (ec.isFlushing()) {
  476. elementSM.flush();
  477. }
  478. } else {
  479. updateElementFk(ownerOP, element, null, -1);
  480. if (deleteElementsOnRemoveOrClear()) {
  481. // TODO If present elsewhere in List then don't delete the element from persistence
  482. ec.deleteObjectInternal(element);
  483. }
  484. }
  485. }
  486. else {
  487. // Delete the element
  488. ec.deleteObjectInternal(element);
  489. }
  490. }
  491. return true;
  492. }
  493. /**
  494. * Internal method to remove an object at a location in the List.
  495. * @param ownerOP ObjectProvider for the owner of the list.
  496. * @param index The location
  497. * @param size Current size of list (if known). -1 if not known
  498. */
  499. protected void removeAt(ObjectProvider ownerOP, int index, int size)
  500. {
  501. if (!indexedList) {
  502. throw new NucleusUserException("Cannot remove an element from a particular position with an ordered list since no indexes exist");
  503. }
  504. // Handle delete/nulling of the element - Use thread-local to prevent recurse
  505. if (removing.get()) {
  506. return;
  507. }
  508. boolean deleteElement = deleteElementsOnRemoveOrClear();
  509. ExecutionContext ec = ownerOP.getExecutionContext();
  510. Object element = get(ownerOP, index);
  511. try {
  512. removing.set(true);
  513. if (!deleteElement) {
  514. // Nullify the index of the element
  515. ObjectProvider elementOP = ec.findObjectProvider(element);
  516. if (elementOP != null && !ec.getApiAdapter().isDeleted(element)) {
  517. Entity elementEntity = getOwnerEntity(elementOP);
  518. if (!storeMgr.storageVersionAtLeast(StorageVersion.READ_OWNED_CHILD_KEYS_FROM_PARENTS)) {
  519. // Remove the external index property from the element
  520. elementEntity.removeProperty(getIndexPropertyName());
  521. }
  522. EntityUtils.putEntityIntoDatastore(ec, elementEntity);
  523. }
  524. } else {
  525. // Delete the element
  526. ec.deleteObjectInternal(element);
  527. }
  528. } finally {
  529. removing.set(false);
  530. }
  531. // TODO Don't bother with this if using latest storage version (but update tests too)
  532. // Not storing element keys in owner, so need to update the index property of following objects
  533. if (orderMapping != null) {
  534. // need to shift indexes of following elements down
  535. DatastoreServiceConfig config = storeMgr.getDefaultDatastoreServiceConfigForReads();
  536. DatastoreService service = DatastoreServiceFactoryInternal.getDatastoreService(config);
  537. AbstractClassMetaData acmd = elementCmd;
  538. String kind =
  539. storeMgr.getIdentifierFactory().newDatastoreContainerIdentifier(acmd).getIdentifierName();
  540. Query q = new Query(kind);
  541. Key key = EntityUtils.getPrimaryKeyAsKey(ec.getApiAdapter(), ownerOP);
  542. q.setAncestor(key);
  543. // create an entity just to capture the name of the index property
  544. Entity entity = new Entity(kind);
  545. orderMapping.setObject(ec, entity, new int[] {1}, index);
  546. String indexProp = entity.getProperties().keySet().iterator().next();
  547. q.addFilter(indexProp, Query.FilterOperator.GREATER_THAN, index);
  548. for (Entity shiftMe : service.prepare(service.getCurrentTransaction(null), q).asIterable()) {
  549. Long pos = (Long) shiftMe.getProperty(indexProp);
  550. shiftMe.setProperty(indexProp, pos - 1);
  551. EntityUtils.putEntityIntoDatastore(ec, shiftMe);
  552. }
  553. }
  554. }
  555. /* (non-Javadoc)
  556. * @see org.datanucleus.store.scostore.CollectionStore#update(org.datanucleus.store.ObjectProvider, java.util.Collection)
  557. */
  558. public void update(ObjectProvider ownerOP, Collection coll) {
  559. if (coll == null || coll.isEmpty()) {
  560. clear(ownerOP);
  561. return;
  562. }
  563. // Find existing elements, and remove any that are no longer present
  564. Collection existing = new ArrayList();
  565. Iterator elemIter = iterator(ownerOP);
  566. while (elemIter.hasNext()) {
  567. Object elem = elemIter.next();
  568. if (!coll.contains(elem)) {
  569. remove(ownerOP, elem, -1, true);
  570. }
  571. else {
  572. existing.add(elem);
  573. }
  574. }
  575. if (existing.equals(coll)) {
  576. // Existing (after any removals) is same as the specified so job done
  577. return;
  578. }
  579. // TODO Improve this - need to allow for list element position changes etc
  580. clear(ownerOP);
  581. addAll(ownerOP, coll, 0);
  582. }
  583. /* (non-Javadoc)
  584. * @see org.datanucleus.store.scostore.ListStore#get(org.datanucleus.store.ObjectProvider, int)
  585. */
  586. public Object get(ObjectProvider op, int index) {
  587. if (MetaDataUtils.readRelatedKeysFromParent(storeMgr, ownerMemberMetaData)) {
  588. // Get child keys from field in owner Entity
  589. ExecutionContext ec = op.getExecutionContext();
  590. Entity datastoreEntity = getOwnerEntity(op);
  591. String propName = EntityUtils.getPropertyName(storeMgr.getIdentifierFactory(), ownerMemberMetaData);
  592. if (datastoreEntity.hasProperty(propName)) {
  593. Object value = datastoreEntity.getProperty(propName);
  594. if (value == null) {
  595. return null;
  596. }
  597. List<Key> keys = (List<Key>)value;
  598. Key indexKey = keys.get(index);
  599. DatastoreServiceConfig config = storeMgr.getDefaultDatastoreServiceConfigForReads();
  600. DatastoreService ds = DatastoreServiceFactoryInternal.getDatastoreService(config);
  601. try {
  602. return EntityUtils.entityToPojo(ds.get(indexKey), elementCmd, clr, ec, false, ec.getFetchPlan());
  603. } catch (EntityNotFoundException enfe) {
  604. throw new NucleusDataStoreException("Could not determine entity for index=" + index + " with key=" + indexKey, enfe);
  605. }
  606. }
  607. } else {
  608. // Earlier storage version, for owned relation, so use parentKey for membership of List
  609. ListIterator iter = listIterator(op, index, index);
  610. if (iter == null || !iter.hasNext()) {
  611. return null;
  612. }
  613. if (!indexedList) {
  614. // Restrict to the actual element since can't be done in the query
  615. Object obj = null;
  616. int position = 0;
  617. while (iter.hasNext()) {
  618. obj = iter.next();
  619. if (position == index) {
  620. return obj;
  621. }
  622. position++;
  623. }
  624. }
  625. return iter.next();
  626. }
  627. return null;
  628. }
  629. /**
  630. * Utility to find the indices of a collection of elements.
  631. * The returned list are in reverse order (highest index first).
  632. * @param op ObjectProvider for the owner of the list
  633. * @param elements The elements
  634. * @return The indices of the elements in the List.
  635. */
  636. protected int[] getIndicesOf(ObjectProvider op, Collection elements)
  637. {
  638. if (elements == null || elements.size() == 0) {
  639. return null;
  640. }
  641. ExecutionContext ec = op.getExecutionContext();
  642. Iterator iter = elements.iterator();
  643. while (iter.hasNext()) {
  644. validateElementForReading(ec, iter.next());
  645. }
  646. // Since the datastore doesn't support 'or', we're going to sort the keys in memory.
  647. // issue an ancestor query that fetches all children between the first key and the last,
  648. // and then build the array of indices from there. The query may return entities that are
  649. // not in the elements so we have to be careful.
  650. if (elements.isEmpty()) {
  651. return new int[0];
  652. }
  653. if (MetaDataUtils.readRelatedKeysFromParent(storeMgr, ownerMemberMetaData)) {
  654. // Obtain via field of List<Key> in parent
  655. String propName = EntityUtils.getPropertyName(storeMgr.getIdentifierFactory(), ownerMemberMetaData);
  656. Entity ownerEntity = getOwnerEntity(op);
  657. if (ownerEntity.hasProperty(propName)) {
  658. Object value = ownerEntity.getProperty(propName);
  659. if (value == null) {
  660. return new int[0];
  661. }
  662. // Convert elements into list of keys to search for
  663. List<Key> keys = (List<Key>) value;
  664. Set<Key> elementKeys = Utils.newHashSet();
  665. for (Object element : elements) {
  666. Key key = EntityUtils.getKeyForObject(element, ec);
  667. if (key != null) {
  668. elementKeys.add(key);
  669. }
  670. }
  671. // Generate indices list for these elements
  672. int i = 0;
  673. List<Integer> indicesList = new ArrayList<Integer>();
  674. for (Key key : keys) {
  675. if (elementKeys.contains(key)) {
  676. indicesList.add(i);
  677. }
  678. i++;
  679. }
  680. int[] indices = new int[indicesList.size()];
  681. i = 0;
  682. for (Integer index : indicesList) {
  683. indices[i++] = index;
  684. }
  685. return indices;
  686. }
  687. } else {
  688. // Owned relation in earlier storage version so use parentKey to determine membership of list
  689. List<Key> keys = Utils.newArrayList();
  690. Set<Key> keySet = Utils.newHashSet();
  691. for (Object ele : elements) {
  692. ApiAdapter apiAdapter = ec.getApiAdapter();
  693. Object keyOrString =
  694. apiAdapter.getTargetKeyForSingleFieldIdentity(apiAdapter.getIdForObject(ele));
  695. Key key = keyOrString instanceof Key ? (Key) keyOrString : KeyFactory.stringToKey((String) keyOrString);
  696. if (key == null) {
  697. throw new NucleusUserException("Collection element does not have a primary key.");
  698. } else if (key.getParent() == null) {
  699. throw new NucleusUserException("Collection element primary key does not have a parent.");
  700. }
  701. keys.add(key);
  702. keySet.add(key);
  703. }
  704. Collections.sort(keys);
  705. AbstractClassMetaData emd = elementCmd;
  706. String kind =
  707. storeMgr.getIdentifierFactory().newDatastoreContainerIdentifier(emd).getIdentifierName();
  708. Query q = new Query(kind);
  709. // This is safe because we know we have at least one element and therefore
  710. // at least one key.
  711. q.setAncestor(keys.get(0).getParent());
  712. q.addFilter(
  713. Entity.KEY_RESERVED_PROPERTY, Query.FilterOperator.GREATER_THAN_OR_EQUAL, keys.get(0));
  714. q.addFilter(
  715. Entity.KEY_RESERVED_PROPERTY, Query.FilterOperator.LESS_THAN_OR_EQUAL, keys.get(keys.size() - 1));
  716. q.addSort(Entity.KEY_RESERVED_PROPERTY, Query.SortDirection.DESCENDING);
  717. DatastoreServiceConfig config = storeMgr.getDefaultDatastoreServiceConfigForReads();
  718. DatastoreService service = DatastoreServiceFactoryInternal.getDatastoreService(config);
  719. int[] indices = new int[keys.size()];
  720. int index = 0;
  721. for (Entity e : service.prepare(service.getCurrentTransaction(null), q).asIterable()) {
  722. if (keySet.contains(e.getKey())) {
  723. Long indexVal = (Long) orderMapping.getObject(ec, e, new int[1]);
  724. if (indexVal == null) {
  725. throw new NucleusDataStoreException("Null index value");
  726. }
  727. indices[index++] = indexVal.intValue();
  728. }
  729. }
  730. if (index != indices.length) {
  731. // something was missing in the result set
  732. throw new NucleusDataStoreException("Too few keys returned.");
  733. }
  734. return indices;
  735. }
  736. return new int[0];
  737. }
  738. /* (non-Javadoc)
  739. * @see org.datanucleus.store.scostore.ListStore#indexOf(org.datanucleus.store.ObjectProvider, java.lang.Object)
  740. */
  741. public int indexOf(ObjectProvider op, Object element) {
  742. ExecutionContext ec = op.getExecutionContext();
  743. validateElementForReading(ec, element);
  744. ObjectProvider elementOP = ec.findObjectProvider(element);
  745. Key elementKey = EntityUtils.getPrimaryKeyAsKey(ec.getApiAdapter(), elementOP);
  746. if (elementKey == null) {
  747. // Not persistent
  748. return -1;
  749. }
  750. if (MetaDataUtils.readRelatedKeysFromParent(storeMgr, ownerMemberMetaData)) {
  751. // Return the position using the field of List<Key> in the owner
  752. String propName = EntityUtils.getPropertyName(storeMgr.getIdentifierFactory(), ownerMemberMetaData);
  753. Entity ownerEntity = getOwnerEntity(op);
  754. if (ownerEntity.hasProperty(propName)) {
  755. Object value = ownerEntity.getProperty(propName);
  756. if (value == null) {
  757. return -1;
  758. }
  759. List<Key> keys = (List<Key>) value;
  760. return keys.indexOf(elementKey);
  761. }
  762. } else {
  763. // Owned relation in earlier storage version so use parentKey to determine membership of list (only present once)
  764. if (elementKey.getParent() == null) {
  765. throw new NucleusUserException("Element primary-key does not have a parent.");
  766. }
  767. DatastoreServiceConfig config = storeMgr.getDefaultDatastoreServiceConfigForReads();
  768. DatastoreService service = DatastoreServiceFactoryInternal.getDatastoreService(config);
  769. try {
  770. Entity e = service.get(elementKey);
  771. Long indexVal = (Long) orderMapping.getObject(ec, e, new int[1]);
  772. if (indexVal == null) {
  773. throw new NucleusDataStoreException("Null index value");
  774. }
  775. return indexVal.intValue();
  776. } catch (EntityNotFoundException enfe) {
  777. throw new NucleusDataStoreException("Could not determine index of entity.", enfe);
  778. }
  779. }
  780. return -1;
  781. }
  782. /* (non-Javadoc)
  783. * @see org.datanucleus.store.scostore.ListStore#lastIndexOf(org.datanucleus.store.ObjectProvider, java.lang.Object)
  784. */
  785. public int lastIndexOf(ObjectProvider op, Object element) {
  786. ExecutionContext ec = op.getExecutionContext();
  787. validateElementForReading(ec, element);
  788. ObjectProvider elementOP = ec.findObjectProvider(element);
  789. Key elementKey = EntityUtils.getPrimaryKeyAsKey(ec.getApiAdapter(), elementOP);
  790. if (elementKey == null) {
  791. // Not persistent
  792. return -1;
  793. }
  794. if (MetaDataUtils.readRelatedKeysFromParent(storeMgr, ownerMemberMetaData)) {
  795. // Return the position using the field of List<Key> in the owner
  796. String propName = EntityUtils.getPropertyName(storeMgr.getIdentifierFactory(), ownerMemberMetaData);
  797. Entity ownerEntity = getOwnerEntity(op);
  798. if (ownerEntity.hasProperty(propName)) {
  799. Object value = ownerEntity.getProperty(propName);
  800. if (value == null) {
  801. return -1;
  802. }
  803. List<Key> keys = (List<Key>) value;
  804. return keys.lastIndexOf(elementKey);
  805. }
  806. }
  807. // Owned relation in earlier storage version so use parentKey to determine membership of list (only present once)
  808. return indexOf(op, element);
  809. }
  810. /* (non-Javadoc)
  811. * @see org.datanucleus.store.scostore.ListStore#set(org.datanucleus.store.ObjectProvider, int, java.lang.Object, boolean)
  812. */
  813. public Object set(ObjectProvider op, int index, Object element, boolean allowCascadeDelete) {
  814. // Get current element at this position
  815. Object obj = get(op, index);
  816. if (MetaDataUtils.isOwnedRelation(ownerMemberMetaData, storeMgr)) {
  817. // Register the parent key for the element when owned
  818. Key parentKey = EntityUtils.getKeyForObject(op.getObject(), op.getExecutionContext());
  819. KeyRegistry.getKeyRegistry(op.getExecutionContext()).registerParentKeyForOwnedObject(element, parentKey);
  820. }
  821. // Make sure the element going to this position is persisted (and give it its index)
  822. validateElementForWriting(op, element, index);
  823. // TODO Allow for a user setting position x as element1 and then setting element2 (that used to be there) to position y
  824. // At the moment we just delete the previous element
  825. if (ownerMemberMetaData.getCollection().isDependentElement() && allowCascadeDelete && obj != null) {
  826. op.getExecutionContext().deleteObjectInternal(obj);
  827. }
  828. return obj;
  829. }
  830. /* (non-Javadoc)
  831. * @see org.datanucleus.store.scostore.ListStore#subList(org.datanucleus.store.ObjectProvider, int, int)
  832. */
  833. public List subList(ObjectProvider op, int startIdx, int endIdx) {
  834. ListIterator iter = listIterator(op, startIdx, endIdx);
  835. java.util.List list = new ArrayList();
  836. while (iter.hasNext()) {
  837. list.add(iter.next());
  838. }
  839. if (!indexedList) {
  840. if (list.size() > (endIdx-startIdx)) {
  841. // Iterator hasn't restricted what is returned so do the index range restriction here
  842. return list.subList(startIdx, endIdx);
  843. }
  844. }
  845. return list;
  846. }
  847. private List<Query.FilterPredicate> getFilterPredicates(int startIdx, int endIdx) {
  848. List<Query.FilterPredicate> filterPredicates = Utils.newArrayList();
  849. if (indexedList) {
  850. String indexProperty = getIndexPropertyName();
  851. if (startIdx >= 0 && endIdx == startIdx) {
  852. // Particular index required so add restriction
  853. Query.FilterPredicate filterPred =
  854. new Query.FilterPredicate(indexProperty, Query.FilterOperator.EQUAL, startIdx);
  855. filterPredicates.add(filterPred);
  856. } else if (startIdx != -1 || endIdx != -1) {
  857. // Add restrictions on start/end indices as required
  858. if (startIdx >= 0) {
  859. Query.FilterPredicate filterPred =
  860. new Query.FilterPredicate(indexProperty, Query.FilterOperator.GREATER_THAN_OR_EQUAL, startIdx);
  861. filterPredicates.add(filterPred);
  862. }
  863. if (endIdx >= 0) {
  864. Query.FilterPredicate filterPred =
  865. new Query.FilterPredicate(indexProperty, Query.FilterOperator.LESS_THAN, endIdx);
  866. filterPredicates.add(filterPred);
  867. }
  868. }
  869. }
  870. return filterPredicates;
  871. }
  872. private String getIndexPropertyName() {
  873. String propertyName;
  874. if (orderMapping.getMemberMetaData() == null) {
  875. // I'm not sure what we should do if this mapping doesn't exist so for now we'll just blow up.
  876. propertyName =
  877. orderMapping.getDatastoreMappings()[0].getDatastoreField().getIdentifier().getIdentifierName();
  878. } else {
  879. propertyName = orderMapping.getMemberMetaData().getName();
  880. AbstractMemberMetaData ammd = orderMapping.getMemberMetaData();
  881. if (ammd.getColumn() != null) {
  882. propertyName = ammd.getColumn();
  883. } else if (ammd.getColumnMetaData() != null && ammd.getColumnMetaData().length == 1) {
  884. propertyName = ammd.getColumnMetaData()[0].getName();
  885. }
  886. }
  887. return propertyName;
  888. }
  889. private List<Query.SortPredicate> getSortPredicates() {
  890. // TODO(maxr) Correctly translate field names to datastore property names
  891. // (embedded fields, overridden column names, etc.)
  892. List<Query.SortPredicate> sortPredicates = Utils.newArrayList();
  893. if (indexedList) {
  894. // Order by the index column
  895. String propertyName = getIndexPropertyName();
  896. Query.SortPredicate sortPredicate =
  897. new Query.SortPredicate(propertyName, Query.SortDirection.ASCENDING);
  898. sortPredicates.add(sortPredicate);
  899. } else {
  900. for (OrderMetaData.FieldOrder fieldOrder : ownerMemberMetaData.getOrderMetaData().getFieldOrders()) {
  901. AbstractMemberMetaData orderMmd = elementCmd.getMetaDataForMember(fieldOrder.getFieldName());
  902. String orderPropName = EntityUtils.getPropertyName(storeMgr.getIdentifierFactory(), orderMmd);
  903. boolean isPrimaryKey = orderMmd.isPrimaryKey();
  904. if (isPrimaryKey) {
  905. if (fieldOrder.isForward() && sortPredicates.isEmpty()) {
  906. // Don't even bother adding if the first sort is id ASC (this is the
  907. // default sort so there's no point in making the datastore figure this out).
  908. break;
  909. }
  910. // sorting by id requires us to use a reserved property name
  911. orderPropName = Entity.KEY_RESERVED_PROPERTY;
  912. }
  913. Query.SortPredicate sortPredicate = new Query.SortPredicate(
  914. orderPropName, fieldOrder.isForward() ? Query.SortDirection.ASCENDING : Query.SortDirection.DESCENDING);
  915. sortPredicates.add(sortPredicate);
  916. if (isPrimaryKey) {
  917. // User wants to sort by pk. Since pk is guaranteed to be unique, break
  918. // because we know there's no point in adding any more sort predicates
  919. break;
  920. }
  921. }
  922. }
  923. return sortPredicates;
  924. }
  925. boolean isPrimaryKey(String propertyName) {
  926. return elementCmd.getMetaDataForMember(propertyName).isPrimaryKey();
  927. }
  928. /**
  929. * Method to validate that an element is valid for writing to the datastore.
  930. * TODO Minimise differences to super.validateElementForWriting()
  931. * @param op ObjectProvider for the owner of the List
  932. * @param element The element to validate
  933. * @param index The position that the element is being stored at in the list
  934. * @return Whether the element was inserted
  935. */
  936. protected boolean validateElementForWriting(final ObjectProvider op, Object element, final int index)
  937. {
  938. final Object newOwner = op.getObject();
  939. // Check if element is ok for use in the datastore, specifying any external mappings that may be required
  940. boolean inserted = super.validateElementForWriting(op.getExecutionContext(), element, new FieldValues()
  941. {
  942. public void fetchFields(ObjectProvider elementOP)
  943. {
  944. // Find the (element) table storing the FK back to the owner
  945. boolean isPersistentInterface = storeMgr.getNucleusContext().getMetaDataManager().isPersistentInterface(elementType);
  946. DatastoreClass elementTable = null;
  947. if (isPersistentInterface) {
  948. elementTable = storeMgr.getDatastoreClass(
  949. storeMgr.getNucleusContext().getMetaDataManager().getImplementationNameForPersistentInterface(elementType), clr);
  950. }
  951. else {
  952. elementTable = storeMgr.getDatastoreClass(elementType, clr);
  953. }
  954. if (elementTable == null) {
  955. // "subclass-table", persisted into table of other class
  956. AbstractClassMetaData[] managingCmds = storeMgr.getClassesManagingTableForClass(elementCmd, clr);
  957. if (managingCmds != null && managingCmds.length > 0) {
  958. // Find which of these subclasses is appropriate for this element
  959. for (int i=0;i<managingCmds.length;i++) {
  960. Class tblCls = clr.classForName(managingCmds[i].getFullClassName());
  961. if (tblCls.isAssignableFrom(elementOP.getObject().getClass())) {
  962. elementTable = storeMgr.getDatastoreClass(managingCmds[i].getFullClassName(), clr);
  963. break;
  964. }
  965. }
  966. }
  967. }
  968. if (elementTable != null) {
  969. JavaTypeMapping externalFKMapping = elementTable.getExternalMapping(ownerMemberMetaData, MappingConsumer.MAPPING_TYPE_EXTERNAL_FK);
  970. if (externalFKMapping != null) {
  971. // The element has an external FK mapping so set the value it needs to use in the INSERT
  972. elementOP.setAssociatedValue(externalFKMapping, op.getObject());
  973. }
  974. if (orderMapping != null && index >= 0) {
  975. if (ownerMemberMetaData.getOrderMetaData() != null && ownerMemberMetaData.getOrderMetaData().getMappedBy() != null) {
  976. // Order is stored in a field in the element so update it
  977. // We support mapped-by fields of types int/long/Integer/Long currently
  978. Object indexValue = null;
  979. if (orderMapping.getMemberMetaData().getTypeName().equals(ClassNameConstants.JAVA_LANG_LONG) ||
  980. orderMapping.getMemberMetaData().getTypeName().equals(ClassNameConstants.LONG)) {
  981. indexValue = Long.valueOf(index);
  982. } else {
  983. indexValue = Integer.valueOf(index);
  984. }
  985. elementOP.replaceFieldMakeDirty(orderMapping.getMemberMetaData().getAbsoluteFieldNumber(), indexValue);
  986. } else {
  987. // Order is stored in a surrogate column so save its vaue for the element to use later
  988. elementOP.setAssociatedValue(orderMapping, Integer.valueOf(index));
  989. }
  990. }
  991. }
  992. if (relationType == RelationType.ONE_TO_MANY_BI) {
  993. // TODO This is ManagedRelations - move into RelationshipManager
  994. // Managed Relations : 1-N bidir, so make sure owner is correct at persist
  995. Object currentOwner = elementOP.provideField(elementMemberMetaData.getAbsoluteFieldNumber());
  996. if (currentOwner == null) {
  997. // No owner, so correct it
  998. NucleusLogger.PERSISTENCE.info(LOCALISER.msg("056037",
  999. StringUtils.toJVMIDString(op.getObject()), ownerMemberMetaData.getFullFieldName(),
  1000. StringUtils.toJVMIDString(elementOP.getObject())));
  1001. elementOP.replaceFieldMakeDirty(elementMemberMetaData.getAbsoluteFieldNumber(), newOwner);
  1002. }
  1003. else if (currentOwner != newOwner && op.getReferencedPC() == null) {
  1004. // Owner of the element is neither this container nor is it being attached
  1005. // Inconsistent owner, so throw exception
  1006. throw new NucleusUserException(LOCALISER.msg("056038",
  1007. StringUtils.toJVMIDString(op.getObject()), ownerMemberMetaData.getFullFieldName(),
  1008. StringUtils.toJVMIDString(elementOP.getObject()),
  1009. StringUtils.toJVMIDString(currentOwner)));
  1010. }
  1011. }
  1012. }
  1013. public void fetchNonLoadedFields(ObjectProvider elementOP) {}
  1014. public FetchPlan getFetchPlanForLoading() { return null; }
  1015. });
  1016. return inserted;
  1017. }
  1018. }