PageRenderTime 163ms CodeModel.GetById 3ms app.highlight 147ms RepoModel.GetById 1ms app.codeStats 1ms

/tests/com/google/appengine/datanucleus/jdo/JDOOneToManyTestCase.java

http://datanucleus-appengine.googlecode.com/
Java | 1713 lines | 1415 code | 213 blank | 85 comment | 96 complexity | dd56daf6ee210431500db8af72891577 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.jdo;
  17
  18import com.google.appengine.api.datastore.DatastoreService;
  19import com.google.appengine.api.datastore.DatastoreServiceConfig;
  20import com.google.appengine.api.datastore.Entity;
  21import com.google.appengine.api.datastore.EntityNotFoundException;
  22import com.google.appengine.api.datastore.Key;
  23import com.google.appengine.api.datastore.KeyFactory;
  24import com.google.appengine.api.datastore.Query;
  25import com.google.appengine.api.datastore.Transaction;
  26import com.google.appengine.api.datastore.TransactionOptions;
  27import com.google.appengine.datanucleus.DatastoreServiceFactoryInternal;
  28import com.google.appengine.datanucleus.DatastoreServiceInterceptor;
  29import com.google.appengine.datanucleus.StorageVersion;
  30import com.google.appengine.datanucleus.Utils;
  31import com.google.appengine.datanucleus.test.jdo.BidirectionalChildJDO;
  32import com.google.appengine.datanucleus.test.jdo.BidirectionalChildLongPkJDO;
  33import com.google.appengine.datanucleus.test.jdo.BidirectionalChildUnencodedStringPkJDO;
  34import com.google.appengine.datanucleus.test.jdo.Flight;
  35import com.google.appengine.datanucleus.test.jdo.HasExplicitIndexColumnJDO;
  36import com.google.appengine.datanucleus.test.jdo.HasKeyPkJDO;
  37import com.google.appengine.datanucleus.test.jdo.HasOneToManyJDO;
  38import com.google.appengine.datanucleus.test.jdo.HasOneToManyKeyPkJDO;
  39import com.google.appengine.datanucleus.test.jdo.HasOneToManyListJDO;
  40import com.google.appengine.datanucleus.test.jdo.HasOneToManyListWithOrderByJDO;
  41import com.google.appengine.datanucleus.test.jdo.HasOneToManyLongPkJDO;
  42import com.google.appengine.datanucleus.test.jdo.HasOneToManyUnencodedStringPkJDO;
  43import com.google.appengine.datanucleus.test.jdo.HasOneToManyWithOrderByJDO;
  44
  45import org.easymock.EasyMock;
  46
  47import java.lang.reflect.Method;
  48import java.util.Collection;
  49import java.util.Collections;
  50import java.util.ConcurrentModificationException;
  51import java.util.Iterator;
  52import java.util.List;
  53import java.util.Set;
  54
  55import javax.jdo.JDOFatalUserException;
  56
  57import static com.google.appengine.datanucleus.TestUtils.assertKeyParentEquals;
  58
  59/**
  60 * @author Max Ross <maxr@google.com>
  61 */
  62abstract class JDOOneToManyTestCase extends JDOTestCase {
  63
  64  @Override
  65  protected void tearDown() throws Exception {
  66    try {
  67      if (!pm.isClosed() && pm.currentTransaction().isActive()) {
  68        pm.currentTransaction().rollback();
  69      }
  70      pmf.close();
  71    } finally {
  72      super.tearDown();
  73    }
  74  }
  75
  76  void testInsert_NewParentAndChild(HasOneToManyJDO parent,
  77      BidirectionalChildJDO bidirChild, StartEnd startEnd) throws EntityNotFoundException {
  78    bidirChild.setChildVal("yam");
  79
  80    Flight f = newFlight();
  81
  82    HasKeyPkJDO hasKeyPk = new HasKeyPkJDO();
  83    hasKeyPk.setStr("yag");
  84
  85    parent.addBidirChild(bidirChild);
  86    bidirChild.setParent(parent);
  87    parent.addFlight(f);
  88    parent.addHasKeyPk(hasKeyPk);
  89    parent.setVal("yar");
  90
  91    startEnd.start();
  92    pm.makePersistent(parent);
  93    startEnd.end();
  94
  95    assertNotNull(bidirChild.getId());
  96    assertNotNull(f.getId());
  97    assertNotNull(hasKeyPk.getKey());
  98
  99    Entity bidirChildEntity = ds.get(KeyFactory.stringToKey(bidirChild.getId()));
 100    assertNotNull(bidirChildEntity);
 101    assertEquals("yam", bidirChildEntity.getProperty("childVal"));
 102    assertEquals(KeyFactory.stringToKey(bidirChild.getId()), bidirChildEntity.getKey());
 103    assertKeyParentEquals(parent.getId(), bidirChildEntity, bidirChild.getId());
 104    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 105      assertEquals(0L, bidirChildEntity.getProperty("bidirChildren_INTEGER_IDX"));
 106    }
 107
 108    Entity flightEntity = ds.get(KeyFactory.stringToKey(f.getId()));
 109    assertNotNull(flightEntity);
 110    assertEquals("bos", flightEntity.getProperty("origin"));
 111    assertEquals("mia", flightEntity.getProperty("dest"));
 112    assertEquals("jimmy", flightEntity.getProperty("name"));
 113    assertEquals(KeyFactory.stringToKey(f.getId()), flightEntity.getKey());
 114    assertKeyParentEquals(parent.getId(), flightEntity, f.getId());
 115    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 116      assertEquals(0L, flightEntity.getProperty("flights_INTEGER_IDX"));
 117    }
 118
 119    Entity hasKeyPkEntity = ds.get(hasKeyPk.getKey());
 120    assertNotNull(hasKeyPkEntity);
 121    assertEquals("yag", hasKeyPkEntity.getProperty("str"));
 122    assertEquals(hasKeyPk.getKey(), hasKeyPkEntity.getKey());
 123    assertKeyParentEquals(parent.getId(), hasKeyPkEntity, hasKeyPk.getKey());
 124    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 125      assertEquals(0L, hasKeyPkEntity.getProperty("hasKeyPks_INTEGER_IDX"));
 126    }
 127
 128    Entity parentEntity = ds.get(KeyFactory.stringToKey(parent.getId()));
 129    assertNotNull(parentEntity);
 130    assertEquals(4, parentEntity.getProperties().size());
 131    assertEquals("yar", parentEntity.getProperty("val"));
 132    assertEquals(Utils.newArrayList(bidirChildEntity.getKey()), parentEntity.getProperty("bidirChildren"));
 133    assertEquals(Utils.newArrayList(flightEntity.getKey()), parentEntity.getProperty("flights"));
 134    assertEquals(Utils.newArrayList(hasKeyPkEntity.getKey()), parentEntity.getProperty("hasKeyPks"));
 135
 136    assertCountsInDatastore(parent.getClass(), bidirChild.getClass(), 1, 1);
 137  }
 138
 139  void testInsert_ExistingParentNewChild(HasOneToManyJDO pojo,
 140      BidirectionalChildJDO bidirChild, StartEnd startEnd) throws EntityNotFoundException {
 141    pojo.setVal("yar");
 142
 143    startEnd.start();
 144    pm.makePersistent(pojo);
 145    startEnd.end();
 146
 147    startEnd.start();
 148    assertNotNull(pojo.getId());
 149    assertTrue(pojo.getFlights().isEmpty());
 150    assertTrue(pojo.getHasKeyPks().isEmpty());
 151    assertTrue(pojo.getBidirChildren().isEmpty());
 152
 153    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 154    assertNotNull(pojoEntity);
 155    assertEquals(4, pojoEntity.getProperties().size());
 156    assertEquals("yar", pojoEntity.getProperty("val"));
 157    assertTrue(pojoEntity.hasProperty("bidirChildren"));
 158    assertNull(pojoEntity.getProperty("bidirChildren"));
 159    assertTrue(pojoEntity.hasProperty("flights"));
 160    assertNull(pojoEntity.getProperty("flights"));
 161    assertTrue(pojoEntity.hasProperty("hasKeyPks"));
 162    assertNull(pojoEntity.getProperty("hasKeyPks"));
 163    startEnd.end();
 164
 165    startEnd.start();
 166    pojo = pm.makePersistent(pojo);
 167    assertEquals("yar", pojo.getVal());
 168    Flight f = newFlight();
 169    pojo.addFlight(f);
 170
 171    HasKeyPkJDO hasKeyPk = new HasKeyPkJDO();
 172    hasKeyPk.setStr("yag");
 173    pojo.addHasKeyPk(hasKeyPk);
 174    bidirChild.setChildVal("yam");
 175    pojo.addBidirChild(bidirChild);
 176    startEnd.end();
 177
 178    startEnd.start();
 179    assertNotNull(bidirChild.getId());
 180    assertNotNull(bidirChild.getParent());
 181    assertNotNull(f.getId());
 182    assertNotNull(hasKeyPk.getKey());
 183    startEnd.end();
 184    
 185    Entity bidirChildEntity = ds.get(KeyFactory.stringToKey(bidirChild.getId()));
 186    assertNotNull(bidirChildEntity);
 187    assertEquals("yam", bidirChildEntity.getProperty("childVal"));
 188    assertEquals(KeyFactory.stringToKey(bidirChild.getId()), bidirChildEntity.getKey());
 189    assertKeyParentEquals(pojo.getId(), bidirChildEntity, bidirChild.getId());
 190    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 191      assertEquals(0L, bidirChildEntity.getProperty("bidirChildren_INTEGER_IDX"));
 192    }
 193
 194    Entity flightEntity = ds.get(KeyFactory.stringToKey(f.getId()));
 195    assertNotNull(flightEntity);
 196    assertEquals("bos", flightEntity.getProperty("origin"));
 197    assertEquals("mia", flightEntity.getProperty("dest"));
 198    assertEquals("jimmy", flightEntity.getProperty("name"));
 199    assertEquals(KeyFactory.stringToKey(f.getId()), flightEntity.getKey());
 200    assertKeyParentEquals(pojo.getId(), flightEntity, f.getId());
 201    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 202      assertEquals(0L, flightEntity.getProperty("flights_INTEGER_IDX"));
 203    }
 204
 205    Entity hasKeyPkEntity = ds.get(hasKeyPk.getKey());
 206    assertNotNull(hasKeyPkEntity);
 207    assertEquals("yag", hasKeyPkEntity.getProperty("str"));
 208    assertEquals(hasKeyPk.getKey(), hasKeyPkEntity.getKey());
 209    assertKeyParentEquals(pojo.getId(), hasKeyPkEntity, hasKeyPk.getKey());
 210    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 211      assertEquals(0L, hasKeyPkEntity.getProperty("hasKeyPks_INTEGER_IDX"));
 212    }
 213
 214    Entity parentEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 215    assertNotNull(parentEntity);
 216    assertEquals(4, parentEntity.getProperties().size());
 217    assertEquals("yar", parentEntity.getProperty("val"));
 218    assertEquals(Utils.newArrayList(bidirChildEntity.getKey()), parentEntity.getProperty("bidirChildren"));
 219    assertEquals(Utils.newArrayList(flightEntity.getKey()), parentEntity.getProperty("flights"));
 220    assertEquals(Utils.newArrayList(hasKeyPkEntity.getKey()), parentEntity.getProperty("hasKeyPks"));
 221
 222    assertCountsInDatastore(pojo.getClass(), bidirChild.getClass(), 1, 1);
 223  }
 224
 225  void testSwapAtPosition(HasOneToManyJDO pojo,
 226      BidirectionalChildJDO bidir1,
 227      BidirectionalChildJDO bidir2, StartEnd startEnd) throws EntityNotFoundException {
 228    pojo.setVal("yar");
 229    bidir2.setChildVal("yam");
 230    Flight f = newFlight();
 231    HasKeyPkJDO hasKeyPk = new HasKeyPkJDO();
 232
 233    pojo.addFlight(f);
 234    pojo.addHasKeyPk(hasKeyPk);
 235    pojo.addBidirChild(bidir1);
 236    bidir1.setParent(pojo);
 237
 238    startEnd.start();
 239    pm.makePersistent(pojo);
 240    startEnd.end();
 241
 242    assertCountsInDatastore(pojo.getClass(), bidir1.getClass(), 1, 1);
 243
 244    startEnd.start();
 245    pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
 246    String bidir1Id = pojo.getBidirChildren().iterator().next().getId();
 247    String flight1Id = pojo.getFlights().iterator().next().getId();
 248    Key hasKeyPk1Key = pojo.getHasKeyPks().iterator().next().getKey();
 249    pojo.addBidirChildAtPosition(bidir2, 0);
 250
 251    Flight f2 = newFlight();
 252    f2.setName("another name");
 253    pojo.addFlightAtPosition(f2, 0);
 254
 255    HasKeyPkJDO hasKeyPk2 = new HasKeyPkJDO();
 256    hasKeyPk2.setStr("another str");
 257    pojo.addHasKeyPkAtPosition(hasKeyPk2, 0);
 258    startEnd.end();
 259
 260    startEnd.start();
 261    pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
 262    assertNotNull(pojo.getId());
 263    assertEquals(1, pojo.getFlights().size());
 264    assertEquals(1, pojo.getHasKeyPks().size());
 265    assertEquals(1, pojo.getBidirChildren().size());
 266    assertNotNull(bidir2.getId());
 267    assertNotNull(bidir2.getParent());
 268    assertNotNull(f2.getId());
 269    assertNotNull(hasKeyPk2.getKey());
 270
 271    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 272    assertNotNull(pojoEntity);
 273    assertEquals(4, pojoEntity.getProperties().size());
 274    assertEquals(Utils.newArrayList(KeyFactory.stringToKey(bidir2.getId())), pojoEntity.getProperty("bidirChildren"));
 275    assertEquals(Utils.newArrayList(KeyFactory.stringToKey(f2.getId())), pojoEntity.getProperty("flights"));
 276    assertEquals(Utils.newArrayList(hasKeyPk2.getKey()), pojoEntity.getProperty("hasKeyPks"));
 277
 278    startEnd.end();
 279
 280    try {
 281      ds.get(KeyFactory.stringToKey(bidir1Id));
 282      fail("expected EntityNotFoundException");
 283    } catch (EntityNotFoundException enfe) {
 284      // good
 285    }
 286    try {
 287      ds.get(KeyFactory.stringToKey(flight1Id));
 288      fail("expected EntityNotFoundException");
 289    } catch (EntityNotFoundException enfe) {
 290      // good
 291    }
 292    try {
 293      ds.get(hasKeyPk1Key);
 294      fail("expected EntityNotFoundException");
 295    } catch (EntityNotFoundException enfe) {
 296      // good
 297    }
 298    Entity bidirChildEntity = ds.get(KeyFactory.stringToKey(bidir2.getId()));
 299    assertNotNull(bidirChildEntity);
 300    assertEquals("yam", bidirChildEntity.getProperty("childVal"));
 301    assertEquals(KeyFactory.stringToKey(bidir2.getId()), bidirChildEntity.getKey());
 302    assertKeyParentEquals(pojo.getId(), bidirChildEntity, bidir2.getId());
 303    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 304      assertEquals(0L, bidirChildEntity.getProperty("bidirChildren_INTEGER_IDX"));
 305    }
 306
 307    Entity flightEntity = ds.get(KeyFactory.stringToKey(f2.getId()));
 308    assertNotNull(flightEntity);
 309    assertEquals("bos", flightEntity.getProperty("origin"));
 310    assertEquals("mia", flightEntity.getProperty("dest"));
 311    assertEquals("another name", flightEntity.getProperty("name"));
 312    assertEquals(KeyFactory.stringToKey(f2.getId()), flightEntity.getKey());
 313    assertKeyParentEquals(pojo.getId(), flightEntity, f2.getId());
 314    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 315      assertEquals(0L, flightEntity.getProperty("flights_INTEGER_IDX"));
 316    }
 317
 318    Entity hasKeyPkEntity = ds.get(hasKeyPk2.getKey());
 319    assertNotNull(hasKeyPkEntity);
 320    assertEquals("another str", hasKeyPkEntity.getProperty("str"));
 321    assertEquals(hasKeyPk2.getKey(), hasKeyPkEntity.getKey());
 322    assertKeyParentEquals(pojo.getId(), hasKeyPkEntity, hasKeyPk2.getKey());
 323    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 324      assertEquals(0L, hasKeyPkEntity.getProperty("hasKeyPks_INTEGER_IDX"));
 325    }
 326
 327    Entity parentEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 328    assertNotNull(parentEntity);
 329    assertEquals(4, parentEntity.getProperties().size());
 330    assertEquals("yar", parentEntity.getProperty("val"));
 331    assertEquals(Utils.newArrayList(bidirChildEntity.getKey()), parentEntity.getProperty("bidirChildren"));
 332    assertEquals(Utils.newArrayList(flightEntity.getKey()), parentEntity.getProperty("flights"));
 333    assertEquals(Utils.newArrayList(hasKeyPkEntity.getKey()), parentEntity.getProperty("hasKeyPks"));
 334
 335    assertCountsInDatastore(pojo.getClass(), bidir2.getClass(), 1, 1);
 336  }
 337
 338  void testRemoveAtPosition(HasOneToManyJDO pojo,
 339      BidirectionalChildJDO bidir1,
 340      BidirectionalChildJDO bidir2,
 341      BidirectionalChildJDO bidir3, StartEnd startEnd) throws EntityNotFoundException {
 342    pojo.setVal("yar");
 343    bidir2.setChildVal("another yam");
 344    bidir3.setChildVal("yet another yam");
 345    Flight f = newFlight();
 346    Flight f2 = newFlight();
 347    Flight f3 = newFlight();
 348    f2.setName("another name");
 349    f3.setName("yet another name");
 350    HasKeyPkJDO hasKeyPk = new HasKeyPkJDO();
 351    HasKeyPkJDO hasKeyPk2 = new HasKeyPkJDO();
 352    HasKeyPkJDO hasKeyPk3 = new HasKeyPkJDO();
 353    hasKeyPk2.setStr("another str");
 354    hasKeyPk3.setStr("yet another str");
 355
 356    pojo.addFlight(f);
 357    pojo.addFlight(f2);
 358    pojo.addFlight(f3);
 359    pojo.addHasKeyPk(hasKeyPk);
 360    pojo.addHasKeyPk(hasKeyPk2);
 361    pojo.addHasKeyPk(hasKeyPk3);
 362    pojo.addBidirChild(bidir1);
 363    pojo.addBidirChild(bidir2);
 364    pojo.addBidirChild(bidir3);
 365
 366    startEnd.start();
 367    pm.makePersistent(pojo);
 368    startEnd.end();
 369
 370    assertCountsInDatastore(pojo.getClass(), bidir1.getClass(), 1, 3);
 371
 372    startEnd.start();
 373    pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
 374    String bidir1Id = pojo.getBidirChildren().iterator().next().getId();
 375    String flight1Id = pojo.getFlights().iterator().next().getId();
 376    Key hasKeyPk1Key = pojo.getHasKeyPks().iterator().next().getKey();
 377    pojo.removeBidirChildAtPosition(0);
 378    pojo.removeFlightAtPosition(0);
 379    pojo.removeHasKeyPkAtPosition(0);
 380    startEnd.end();
 381
 382    startEnd.start();
 383    assertNotNull(pojo.getId());
 384    assertEquals(2, pojo.getFlights().size());
 385    assertEquals(2, pojo.getHasKeyPks().size());
 386    assertEquals(2, pojo.getBidirChildren().size());
 387
 388    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 389    assertNotNull(pojoEntity);
 390    assertEquals(4, pojoEntity.getProperties().size());
 391    assertEquals(Utils.newArrayList(
 392        KeyFactory.stringToKey(bidir2.getId()),
 393        KeyFactory.stringToKey(bidir3.getId())), pojoEntity.getProperty("bidirChildren"));
 394    assertEquals(Utils.newArrayList(
 395        KeyFactory.stringToKey(f2.getId()),
 396        KeyFactory.stringToKey(f3.getId())), pojoEntity.getProperty("flights"));
 397    assertEquals(Utils.newArrayList(
 398        hasKeyPk2.getKey(),
 399        hasKeyPk3.getKey()), pojoEntity.getProperty("hasKeyPks"));
 400
 401    startEnd.end();
 402
 403    try {
 404      ds.get(KeyFactory.stringToKey(bidir1Id));
 405      fail("expected EntityNotFoundException");
 406    } catch (EntityNotFoundException enfe) {
 407      // good
 408    }
 409    try {
 410      ds.get(KeyFactory.stringToKey(flight1Id));
 411      fail("expected EntityNotFoundException");
 412    } catch (EntityNotFoundException enfe) {
 413      // good
 414    }
 415    try {
 416      ds.get(hasKeyPk1Key);
 417      fail("expected EntityNotFoundException");
 418    } catch (EntityNotFoundException enfe) {
 419      // good
 420    }
 421    Entity bidirChildEntity = ds.get(KeyFactory.stringToKey(bidir2.getId()));
 422    assertNotNull(bidirChildEntity);
 423    assertEquals("another yam", bidirChildEntity.getProperty("childVal"));
 424    assertEquals(KeyFactory.stringToKey(bidir2.getId()), bidirChildEntity.getKey());
 425    assertKeyParentEquals(pojo.getId(), bidirChildEntity, bidir2.getId());
 426    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 427      assertEquals(0L, bidirChildEntity.getProperty("bidirChildren_INTEGER_IDX"));
 428    }
 429
 430    bidirChildEntity = ds.get(KeyFactory.stringToKey(bidir3.getId()));
 431    assertNotNull(bidirChildEntity);
 432    assertEquals("yet another yam", bidirChildEntity.getProperty("childVal"));
 433    assertEquals(KeyFactory.stringToKey(bidir3.getId()), bidirChildEntity.getKey());
 434    assertKeyParentEquals(pojo.getId(), bidirChildEntity, bidir2.getId());
 435    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 436      assertEquals(1L, bidirChildEntity.getProperty("bidirChildren_INTEGER_IDX"));
 437    }
 438
 439    Entity flightEntity = ds.get(KeyFactory.stringToKey(f2.getId()));
 440    assertNotNull(flightEntity);
 441    assertEquals("bos", flightEntity.getProperty("origin"));
 442    assertEquals("mia", flightEntity.getProperty("dest"));
 443    assertEquals("another name", flightEntity.getProperty("name"));
 444    assertEquals(KeyFactory.stringToKey(f2.getId()), flightEntity.getKey());
 445    assertKeyParentEquals(pojo.getId(), flightEntity, f2.getId());
 446    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 447      assertEquals(0L, flightEntity.getProperty("flights_INTEGER_IDX"));
 448    }
 449
 450    flightEntity = ds.get(KeyFactory.stringToKey(f3.getId()));
 451    assertNotNull(flightEntity);
 452    assertEquals("bos", flightEntity.getProperty("origin"));
 453    assertEquals("mia", flightEntity.getProperty("dest"));
 454    assertEquals("yet another name", flightEntity.getProperty("name"));
 455    assertEquals(KeyFactory.stringToKey(f3.getId()), flightEntity.getKey());
 456    assertKeyParentEquals(pojo.getId(), flightEntity, f2.getId());
 457    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 458      assertEquals(1L, flightEntity.getProperty("flights_INTEGER_IDX"));
 459    }
 460
 461    Entity hasKeyPkEntity = ds.get(hasKeyPk2.getKey());
 462    assertNotNull(hasKeyPkEntity);
 463    assertEquals("another str", hasKeyPkEntity.getProperty("str"));
 464    assertEquals(hasKeyPk2.getKey(), hasKeyPkEntity.getKey());
 465    assertKeyParentEquals(pojo.getId(), hasKeyPkEntity, hasKeyPk2.getKey());
 466    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 467      assertEquals(0L, hasKeyPkEntity.getProperty("hasKeyPks_INTEGER_IDX"));
 468    }
 469
 470    hasKeyPkEntity = ds.get(hasKeyPk3.getKey());
 471    assertNotNull(hasKeyPkEntity);
 472    assertEquals("yet another str", hasKeyPkEntity.getProperty("str"));
 473    assertEquals(hasKeyPk3.getKey(), hasKeyPkEntity.getKey());
 474    assertKeyParentEquals(pojo.getId(), hasKeyPkEntity, hasKeyPk2.getKey());
 475    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 476      assertEquals(1L, hasKeyPkEntity.getProperty("hasKeyPks_INTEGER_IDX"));
 477    }
 478
 479    Entity parentEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 480    assertNotNull(parentEntity);
 481    assertEquals(4, parentEntity.getProperties().size());
 482    assertEquals("yar", parentEntity.getProperty("val"));
 483    assertEquals(Utils.newArrayList(
 484        KeyFactory.stringToKey(bidir2.getId()),
 485        KeyFactory.stringToKey(bidir3.getId())), parentEntity.getProperty("bidirChildren"));
 486    assertEquals(Utils.newArrayList(
 487        KeyFactory.stringToKey(f2.getId()),
 488        KeyFactory.stringToKey(f3.getId())), parentEntity.getProperty("flights"));
 489    assertEquals(Utils.newArrayList(
 490        hasKeyPk2.getKey(),
 491        hasKeyPk3.getKey()), parentEntity.getProperty("hasKeyPks"));
 492
 493    assertCountsInDatastore(pojo.getClass(), bidir2.getClass(), 1, 2);
 494  }
 495
 496  void testAddAtPosition(HasOneToManyJDO pojo,
 497      BidirectionalChildJDO bidir1,
 498      BidirectionalChildJDO bidir2, StartEnd startEnd) throws EntityNotFoundException {
 499    pojo.setVal("yar");
 500    bidir2.setChildVal("yam");
 501    Flight f = newFlight();
 502    Flight f2 = newFlight();
 503    f2.setName("another name");
 504    HasKeyPkJDO hasKeyPk = new HasKeyPkJDO();
 505    HasKeyPkJDO hasKeyPk2 = new HasKeyPkJDO();
 506    hasKeyPk2.setStr("another str");
 507
 508    pojo.addFlight(f);
 509    pojo.addHasKeyPk(hasKeyPk);
 510    pojo.addBidirChild(bidir1);
 511
 512    startEnd.start();
 513    pm.makePersistent(pojo);
 514    startEnd.end();
 515
 516    assertCountsInDatastore(pojo.getClass(), bidir1.getClass(), 1, 1);
 517
 518    startEnd.start();
 519    pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
 520    String bidir1Id = pojo.getBidirChildren().iterator().next().getId();
 521    String flight1Id = pojo.getFlights().iterator().next().getId();
 522    Key hasKeyPk1Key = pojo.getHasKeyPks().iterator().next().getKey();
 523    pojo.addAtPosition(0, bidir2);
 524    pojo.addAtPosition(0, f2);
 525    pojo.addAtPosition(0, hasKeyPk2);
 526    startEnd.end();
 527
 528    startEnd.start();
 529    pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
 530    assertNotNull(pojo.getId());
 531    assertEquals(2, pojo.getFlights().size());
 532    assertEquals(2, pojo.getHasKeyPks().size());
 533    assertEquals(2, pojo.getBidirChildren().size());
 534    assertNotNull(bidir2.getId());
 535    assertNotNull(bidir2.getParent());
 536    assertNotNull(f2.getId());
 537    assertNotNull(hasKeyPk2.getKey());
 538
 539    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 540    assertNotNull(pojoEntity);
 541    assertEquals(4, pojoEntity.getProperties().size());
 542    assertEquals(Utils.newArrayList(
 543        KeyFactory.stringToKey(bidir2.getId()),
 544        KeyFactory.stringToKey(bidir1.getId())), pojoEntity.getProperty("bidirChildren"));
 545    assertEquals(Utils.newArrayList(
 546        KeyFactory.stringToKey(f2.getId()),
 547        KeyFactory.stringToKey(f.getId())), pojoEntity.getProperty("flights"));
 548    assertEquals(Utils.newArrayList(
 549        hasKeyPk2.getKey(),
 550        hasKeyPk.getKey()), pojoEntity.getProperty("hasKeyPks"));
 551
 552    startEnd.end();
 553
 554    ds.get(KeyFactory.stringToKey(bidir1Id));
 555    ds.get(KeyFactory.stringToKey(flight1Id));
 556    ds.get(hasKeyPk1Key);
 557    Entity bidirChildEntity = ds.get(KeyFactory.stringToKey(bidir2.getId()));
 558    assertNotNull(bidirChildEntity);
 559    assertEquals("yam", bidirChildEntity.getProperty("childVal"));
 560    assertEquals(KeyFactory.stringToKey(bidir2.getId()), bidirChildEntity.getKey());
 561    assertKeyParentEquals(pojo.getId(), bidirChildEntity, bidir2.getId());
 562    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 563      assertEquals(0L, bidirChildEntity.getProperty("bidirChildren_INTEGER_IDX"));
 564    }
 565
 566    Entity flightEntity = ds.get(KeyFactory.stringToKey(f2.getId()));
 567    assertNotNull(flightEntity);
 568    assertEquals("bos", flightEntity.getProperty("origin"));
 569    assertEquals("mia", flightEntity.getProperty("dest"));
 570    assertEquals("another name", flightEntity.getProperty("name"));
 571    assertEquals(KeyFactory.stringToKey(f2.getId()), flightEntity.getKey());
 572    assertKeyParentEquals(pojo.getId(), flightEntity, f2.getId());
 573    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 574      assertEquals(0L, flightEntity.getProperty("flights_INTEGER_IDX"));
 575    }
 576
 577    Entity hasKeyPkEntity = ds.get(hasKeyPk2.getKey());
 578    assertNotNull(hasKeyPkEntity);
 579    assertEquals("another str", hasKeyPkEntity.getProperty("str"));
 580    assertEquals(hasKeyPk2.getKey(), hasKeyPkEntity.getKey());
 581    assertKeyParentEquals(pojo.getId(), hasKeyPkEntity, hasKeyPk2.getKey());
 582    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 583      assertEquals(0L, hasKeyPkEntity.getProperty("hasKeyPks_INTEGER_IDX"));
 584    }
 585
 586    Entity parentEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 587    assertNotNull(parentEntity);
 588    assertEquals(4, parentEntity.getProperties().size());
 589    assertEquals("yar", parentEntity.getProperty("val"));
 590    assertEquals(Utils.newArrayList(
 591        KeyFactory.stringToKey(bidir2.getId()),
 592        KeyFactory.stringToKey(bidir1.getId())), parentEntity.getProperty("bidirChildren"));
 593    assertEquals(Utils.newArrayList(
 594        KeyFactory.stringToKey(f2.getId()),
 595        KeyFactory.stringToKey(f.getId())), parentEntity.getProperty("flights"));
 596    assertEquals(Utils.newArrayList(
 597        hasKeyPk2.getKey(),
 598        hasKeyPk.getKey()), parentEntity.getProperty("hasKeyPks"));
 599
 600    assertCountsInDatastore(pojo.getClass(), bidir2.getClass(), 1, 2);
 601  }
 602
 603  void testUpdate_UpdateChildWithMerge(HasOneToManyJDO pojo,
 604      BidirectionalChildJDO bidir, StartEnd startEnd) throws EntityNotFoundException {
 605    Flight f = newFlight();
 606    HasKeyPkJDO hasKeyPk = new HasKeyPkJDO();
 607
 608    pojo.addFlight(f);
 609    pojo.addHasKeyPk(hasKeyPk);
 610    pojo.addBidirChild(bidir);
 611    bidir.setParent(pojo);
 612
 613    startEnd.start();
 614    pm.makePersistent(pojo);
 615    startEnd.end();
 616
 617    assertNotNull(f.getId());
 618    assertNotNull(hasKeyPk.getKey());
 619    assertNotNull(bidir.getId());
 620    assertNotNull(pojo.getId());
 621
 622    startEnd.start();
 623    f.setOrigin("yam");
 624    hasKeyPk.setStr("yar");
 625    bidir.setChildVal("yap");
 626    pm.makePersistent(pojo);
 627    startEnd.end();
 628
 629    Entity flightEntity = ds.get(KeyFactory.stringToKey(f.getId()));
 630    assertNotNull(flightEntity);
 631    assertEquals("yam", flightEntity.getProperty("origin"));
 632    assertKeyParentEquals(pojo.getId(), flightEntity, f.getId());
 633    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 634      assertEquals(0L, flightEntity.getProperty("flights_INTEGER_IDX"));
 635    }
 636
 637    Entity hasKeyPkEntity = ds.get(hasKeyPk.getKey());
 638    assertNotNull(hasKeyPkEntity);
 639    assertEquals("yar", hasKeyPkEntity.getProperty("str"));
 640    assertKeyParentEquals(pojo.getId(), hasKeyPkEntity, hasKeyPk.getKey());
 641    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 642      assertEquals(0L, hasKeyPkEntity.getProperty("hasKeyPks_INTEGER_IDX"));
 643    }
 644
 645    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidir.getId()));
 646    assertNotNull(bidirEntity);
 647    assertEquals("yap", bidirEntity.getProperty("childVal"));
 648    assertKeyParentEquals(pojo.getId(), bidirEntity, bidir.getId());
 649    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 650      assertEquals(0L, bidirEntity.getProperty("bidirChildren_INTEGER_IDX"));
 651    }
 652
 653    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), 1, 1);
 654  }
 655
 656  void testUpdate_UpdateChild(HasOneToManyJDO pojo,
 657      BidirectionalChildJDO bidir, StartEnd startEnd) throws EntityNotFoundException {
 658    Flight f = newFlight();
 659    HasKeyPkJDO hasKeyPk = new HasKeyPkJDO();
 660
 661    pojo.addFlight(f);
 662    pojo.addHasKeyPk(hasKeyPk);
 663    pojo.addBidirChild(bidir);
 664    bidir.setParent(pojo);
 665
 666    startEnd.start();
 667    pm.makePersistent(pojo);
 668    startEnd.end();
 669
 670    assertNotNull(f.getId());
 671    assertNotNull(hasKeyPk.getKey());
 672    assertNotNull(bidir.getId());
 673    assertNotNull(pojo.getId());
 674
 675    startEnd.start();
 676    pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
 677    pojo.getFlights().iterator().next().setOrigin("yam");
 678    pojo.getHasKeyPks().iterator().next().setStr("yar");
 679    pojo.getBidirChildren().iterator().next().setChildVal("yap");
 680    startEnd.end();
 681
 682    Entity flightEntity = ds.get(KeyFactory.stringToKey(f.getId()));
 683    assertNotNull(flightEntity);
 684    assertEquals("yam", flightEntity.getProperty("origin"));
 685    assertKeyParentEquals(pojo.getId(), flightEntity, f.getId());
 686    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 687      assertEquals(0L, flightEntity.getProperty("flights_INTEGER_IDX"));
 688    }
 689
 690    Entity hasKeyPkEntity = ds.get(hasKeyPk.getKey());
 691    assertNotNull(hasKeyPkEntity);
 692    assertEquals("yar", hasKeyPkEntity.getProperty("str"));
 693    assertKeyParentEquals(pojo.getId(), hasKeyPkEntity, hasKeyPk.getKey());
 694    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 695      assertEquals(0L, hasKeyPkEntity.getProperty("hasKeyPks_INTEGER_IDX"));
 696    }
 697
 698    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidir.getId()));
 699    assertNotNull(bidirEntity);
 700    assertEquals("yap", bidirEntity.getProperty("childVal"));
 701    assertKeyParentEquals(pojo.getId(), bidirEntity, bidir.getId());
 702    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
 703      assertEquals(0L, bidirEntity.getProperty("bidirChildren_INTEGER_IDX"));
 704    }
 705
 706    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), 1, 1);
 707  }
 708
 709  void testUpdate_NullOutChildren(HasOneToManyJDO pojo,
 710    BidirectionalChildJDO bidir, StartEnd startEnd) throws EntityNotFoundException {
 711    Flight f = newFlight();
 712    HasKeyPkJDO hasKeyPk = new HasKeyPkJDO();
 713
 714    pojo.addFlight(f);
 715    pojo.addHasKeyPk(hasKeyPk);
 716    pojo.addBidirChild(bidir);
 717    bidir.setParent(pojo);
 718
 719    startEnd.start();
 720    pm.makePersistent(pojo);
 721    startEnd.end();
 722
 723    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), 1, 1);
 724
 725    startEnd.start();
 726    String flightId = f.getId();
 727    Key hasKeyPkKey = hasKeyPk.getKey();
 728    String bidirChildId = bidir.getId();
 729
 730    pojo.nullFlights();
 731    pojo.nullHasKeyPks();
 732    pojo.nullBidirChildren();
 733    pm.makePersistent(pojo);
 734    startEnd.end();
 735
 736    try {
 737      ds.get(KeyFactory.stringToKey(flightId));
 738      fail("expected enfe");
 739    } catch (EntityNotFoundException enfe) {
 740      // good
 741    }
 742
 743    try {
 744      ds.get(hasKeyPkKey);
 745      fail("expected enfe");
 746    } catch (EntityNotFoundException enfe) {
 747      // good
 748    }
 749
 750    try {
 751      ds.get(KeyFactory.stringToKey(bidirChildId));
 752      fail("expected enfe");
 753    } catch (EntityNotFoundException enfe) {
 754      // good
 755    }
 756
 757    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 758    assertEquals(4, pojoEntity.getProperties().size());
 759    assertTrue(pojoEntity.hasProperty("bidirChildren"));
 760    assertNull(pojoEntity.getProperty("bidirChildren"));
 761    assertTrue(pojoEntity.hasProperty("flights"));
 762    assertNull(pojoEntity.getProperty("flights"));
 763    assertTrue(pojoEntity.hasProperty("hasKeyPks"));
 764    assertNull(pojoEntity.getProperty("hasKeyPks"));
 765
 766    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), 1, 0);
 767  }
 768
 769  void testUpdate_ClearOutChildren(HasOneToManyJDO pojo,
 770      BidirectionalChildJDO bidir, StartEnd startEnd) throws EntityNotFoundException {
 771    Flight f = newFlight();
 772    HasKeyPkJDO hasKeyPk = new HasKeyPkJDO();
 773
 774    pojo.addFlight(f);
 775    pojo.addHasKeyPk(hasKeyPk);
 776    pojo.addBidirChild(bidir);
 777    bidir.setParent(pojo);
 778
 779    startEnd.start();
 780    pm.makePersistent(pojo);
 781    startEnd.end();
 782    String flightId = f.getId();
 783    Key hasKeyPkId = hasKeyPk.getKey();
 784    String bidirChildId = bidir.getId();
 785    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), 1, 1);
 786
 787    startEnd.start();
 788    pojo = pm.makePersistent(pojo);
 789    pojo.clearFlights();
 790    pojo.clearHasKeyPks();
 791    pojo.clearBidirChildren();
 792    startEnd.end();
 793
 794    try {
 795      ds.get(KeyFactory.stringToKey(flightId));
 796      fail("expected enfe");
 797    } catch (EntityNotFoundException enfe) {
 798      // good
 799    }
 800
 801    try {
 802      ds.get(hasKeyPkId);
 803      fail("expected enfe");
 804    } catch (EntityNotFoundException enfe) {
 805      // good
 806    }
 807
 808    try {
 809      ds.get(KeyFactory.stringToKey(bidirChildId));
 810      fail("expected enfe");
 811    } catch (EntityNotFoundException enfe) {
 812      // good
 813    }
 814
 815    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 816    assertEquals(4, pojoEntity.getProperties().size());
 817    assertTrue(pojoEntity.hasProperty("bidirChildren"));
 818    assertNull(pojoEntity.getProperty("bidirChildren"));
 819    assertTrue(pojoEntity.hasProperty("flights"));
 820    assertNull(pojoEntity.getProperty("flights"));
 821    assertTrue(pojoEntity.hasProperty("hasKeyPks"));
 822    assertNull(pojoEntity.getProperty("hasKeyPks"));
 823
 824    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), 1, 0);
 825  }
 826
 827  void testFindWithOrderBy(Class<? extends HasOneToManyWithOrderByJDO> pojoClass, StartEnd startEnd)
 828      throws EntityNotFoundException {
 829    getExecutionContext().getNucleusContext().getPersistenceConfiguration().setProperty(
 830        "datanucleus.appengine.allowMultipleRelationsOfSameType", true);
 831    Entity pojoEntity = new Entity(pojoClass.getSimpleName());
 832    ds.put(pojoEntity);
 833
 834    Entity flightEntity1 = newFlightEntity(pojoEntity.getKey(), "bos1", "mia2", "name 1");
 835    flightEntity1.setProperty("flightsByOrigAndDest_INTEGER_IDX", 0);
 836    flightEntity1.setProperty("flightsByIdAndOrig_INTEGER_IDX", 0);
 837    flightEntity1.setProperty("flightsByOrigAndId_INTEGER_IDX", 0);
 838    ds.put(flightEntity1);
 839
 840    Entity flightEntity2 = newFlightEntity(pojoEntity.getKey(), "bos2", "mia2", "name 2");
 841    flightEntity2.setProperty("flightsByOrigAndDest_INTEGER_IDX", 1);
 842    flightEntity2.setProperty("flightsByIdAndOrig_INTEGER_IDX", 1);
 843    flightEntity2.setProperty("flightsByOrigAndId_INTEGER_IDX", 1);
 844    ds.put(flightEntity2);
 845
 846    Entity flightEntity3 = newFlightEntity(pojoEntity.getKey(), "bos1", "mia1", "name 0");
 847    flightEntity3.setProperty("flightsByOrigAndDest_INTEGER_IDX", 2);
 848    flightEntity3.setProperty("flightsByIdAndOrig_INTEGER_IDX", 2);
 849    flightEntity3.setProperty("flightsByOrigAndId_INTEGER_IDX", 2);
 850    ds.put(flightEntity3);
 851
 852    Entity explicitIndexEntity1 =
 853        new Entity(HasExplicitIndexColumnJDO.class.getSimpleName(), pojoEntity.getKey());
 854    explicitIndexEntity1.setProperty("index", 3);
 855    ds.put(explicitIndexEntity1);
 856
 857    Entity explicitIndexEntity2 =
 858        new Entity(HasExplicitIndexColumnJDO.class.getSimpleName(), pojoEntity.getKey());
 859    explicitIndexEntity2.setProperty("index", 2);
 860    ds.put(explicitIndexEntity2);
 861
 862    Entity explicitIndexEntity3 =
 863        new Entity(HasExplicitIndexColumnJDO.class.getSimpleName(), pojoEntity.getKey());
 864    explicitIndexEntity3.setProperty("index", 1);
 865    ds.put(explicitIndexEntity3);
 866
 867    pojoEntity.setProperty("flightsByOrigAndDest", Utils.newArrayList(flightEntity2.getKey(), flightEntity3.getKey(), flightEntity1.getKey()));
 868    pojoEntity.setProperty("flightsByIdAndOrig", Utils.newArrayList(flightEntity3.getKey(), flightEntity2.getKey(), flightEntity1.getKey()));
 869    pojoEntity.setProperty("flightsByOrigAndId", Utils.newArrayList(flightEntity2.getKey(), flightEntity1.getKey(), flightEntity3.getKey()));
 870    pojoEntity.setProperty("hasIndexColumn", Utils.newArrayList(explicitIndexEntity3.getKey(), explicitIndexEntity2.getKey(), explicitIndexEntity1.getKey()));
 871    ds.put(pojoEntity);
 872
 873    startEnd.start();
 874    HasOneToManyWithOrderByJDO pojo = pm.getObjectById(
 875        pojoClass, KeyFactory.keyToString(pojoEntity.getKey()));
 876    assertNotNull(pojo);
 877    assertNotNull(pojo.getFlightsByOrigAndDest());
 878    assertEquals(3, pojo.getFlightsByOrigAndDest().size());
 879    assertEquals("name 2", pojo.getFlightsByOrigAndDest().get(0).getName());
 880    assertEquals("name 0", pojo.getFlightsByOrigAndDest().get(1).getName());
 881    assertEquals("name 1", pojo.getFlightsByOrigAndDest().get(2).getName());
 882
 883    assertNotNull(pojo.getFlightsByIdAndOrig());
 884    assertEquals(3, pojo.getFlightsByIdAndOrig().size());
 885    assertEquals("name 0", pojo.getFlightsByIdAndOrig().get(0).getName());
 886    assertEquals("name 2", pojo.getFlightsByIdAndOrig().get(1).getName());
 887    assertEquals("name 1", pojo.getFlightsByIdAndOrig().get(2).getName());
 888
 889    assertNotNull(pojo.getFlightsByOrigAndId());
 890    assertEquals(3, pojo.getFlightsByOrigAndId().size());
 891    assertEquals("name 2", pojo.getFlightsByOrigAndId().get(0).getName());
 892    assertEquals("name 1", pojo.getFlightsByOrigAndId().get(1).getName());
 893    assertEquals("name 0", pojo.getFlightsByOrigAndId().get(2).getName());
 894
 895    assertNotNull(pojo.getHasIndexColumn());
 896    assertEquals(3, pojo.getHasIndexColumn().size());
 897    assertEquals(explicitIndexEntity3.getKey(), pojo.getHasIndexColumn().get(0).getId());
 898    assertEquals(explicitIndexEntity2.getKey(), pojo.getHasIndexColumn().get(1).getId());
 899    assertEquals(explicitIndexEntity1.getKey(), pojo.getHasIndexColumn().get(2).getId());
 900
 901    startEnd.end();
 902  }
 903
 904  void testFind(Class<? extends HasOneToManyJDO> pojoClass,
 905      Class<? extends BidirectionalChildJDO> bidirClass, StartEnd startEnd) throws EntityNotFoundException {
 906    Entity pojoEntity = new Entity(pojoClass.getSimpleName());
 907    ds.put(pojoEntity);
 908
 909    Entity flightEntity = newFlightEntity(pojoEntity.getKey(), "bos1", "mia2", "name 1");
 910    ds.put(flightEntity);
 911
 912    Entity hasKeyPkEntity = new Entity(HasKeyPkJDO.class.getSimpleName(), pojoEntity.getKey());
 913    hasKeyPkEntity.setProperty("str", "yar");
 914    hasKeyPkEntity.setProperty("hasKeyPks_INTEGER_IDX", 1);
 915    ds.put(hasKeyPkEntity);
 916
 917    Entity bidirEntity = new Entity(bidirClass.getSimpleName(), pojoEntity.getKey());
 918    bidirEntity.setProperty("childVal", "yap");
 919    bidirEntity.setProperty("bidirChildren_INTEGER_IDX", 1);
 920    ds.put(bidirEntity);
 921
 922    pojoEntity.setProperty("bidirChildren", Utils.newArrayList(bidirEntity.getKey()));
 923    pojoEntity.setProperty("hasKeyPks", Utils.newArrayList(hasKeyPkEntity.getKey()));
 924    pojoEntity.setProperty("flights", Utils.newArrayList(flightEntity.getKey()));
 925    ds.put(pojoEntity);
 926
 927    startEnd.start();
 928    HasOneToManyJDO pojo =
 929        pm.getObjectById(pojoClass, KeyFactory.keyToString(pojoEntity.getKey()));
 930    assertNotNull(pojo);
 931    assertNotNull(pojo.getFlights());
 932    assertEquals(1, pojo.getFlights().size());
 933    assertEquals("bos1", pojo.getFlights().iterator().next().getOrigin());
 934    assertNotNull(pojo.getHasKeyPks());
 935    assertEquals(1, pojo.getHasKeyPks().size());
 936    assertEquals("yar", pojo.getHasKeyPks().iterator().next().getStr());
 937    assertNotNull(pojo.getBidirChildren());
 938    assertEquals(1, pojo.getBidirChildren().size());
 939    assertEquals("yap", pojo.getBidirChildren().iterator().next().getChildVal());
 940    assertEquals(pojo, pojo.getBidirChildren().iterator().next().getParent());
 941    startEnd.end();
 942  }
 943
 944  void testQuery(Class<? extends HasOneToManyJDO> pojoClass,
 945      Class<? extends BidirectionalChildJDO> bidirClass, StartEnd startEnd) throws EntityNotFoundException {
 946    Entity pojoEntity = new Entity(pojoClass.getSimpleName());
 947    ds.put(pojoEntity);
 948
 949    Entity flightEntity = newFlightEntity(pojoEntity.getKey(), "bos", "mia2", "name");
 950    ds.put(flightEntity);
 951
 952    Entity hasKeyPkEntity = new Entity(HasKeyPkJDO.class.getSimpleName(), pojoEntity.getKey());
 953    hasKeyPkEntity.setProperty("str", "yar");
 954    hasKeyPkEntity.setProperty("hasKeyPks_INTEGER_IDX", 1);
 955    ds.put(hasKeyPkEntity);
 956
 957    Entity bidirEntity = new Entity(bidirClass.getSimpleName(), pojoEntity.getKey());
 958    bidirEntity.setProperty("childVal", "yap");
 959    bidirEntity.setProperty("bidirChildren_INTEGER_IDX", 1);
 960    ds.put(bidirEntity);
 961
 962    pojoEntity.setProperty("bidirChildren", Utils.newArrayList(bidirEntity.getKey()));
 963    pojoEntity.setProperty("hasKeyPks", Utils.newArrayList(hasKeyPkEntity.getKey()));
 964    pojoEntity.setProperty("flights", Utils.newArrayList(flightEntity.getKey()));
 965    ds.put(pojoEntity);
 966
 967    javax.jdo.Query q = pm.newQuery(
 968        "select from " + pojoClass.getName() + " where id == key parameters String key");
 969    startEnd.start();
 970    @SuppressWarnings("unchecked")
 971    List<HasOneToManyListJDO> result =
 972        (List<HasOneToManyListJDO>) q.execute(KeyFactory.keyToString(pojoEntity.getKey()));
 973    assertEquals(1, result.size());
 974    HasOneToManyJDO pojo = result.get(0);
 975    assertNotNull(pojo.getFlights());
 976    assertEquals(1, pojo.getFlights().size());
 977    assertEquals("bos", pojo.getFlights().iterator().next().getOrigin());
 978    assertEquals(1, pojo.getFlights().size());
 979    assertNotNull(pojo.getHasKeyPks());
 980    assertEquals(1, pojo.getHasKeyPks().size());
 981    assertEquals("yar", pojo.getHasKeyPks().iterator().next().getStr());
 982    assertNotNull(pojo.getBidirChildren());
 983    assertEquals(1, pojo.getBidirChildren().size());
 984    assertEquals("yap", pojo.getBidirChildren().iterator().next().getChildVal());
 985    assertEquals(pojo, pojo.getBidirChildren().iterator().next().getParent());
 986    startEnd.end();
 987  }
 988
 989  void testChildFetchedLazily(Class<? extends HasOneToManyJDO> pojoClass,
 990      Class<? extends BidirectionalChildJDO> bidirClass) throws Exception {
 991    DatastoreServiceConfig config = getStoreManager().getDefaultDatastoreServiceConfigForReads();
 992    tearDown();
 993    DatastoreService mockDatastore = EasyMock.createMock(DatastoreService.class);
 994    DatastoreService original = DatastoreServiceFactoryInternal.getDatastoreService(config);
 995    DatastoreServiceFactoryInternal.setDatastoreService(mockDatastore);
 996    try {
 997      setUp();
 998
 999      Entity pojoEntity = new Entity(pojoClass.getSimpleName());
1000      ds.put(pojoEntity);
1001
1002      Entity FlightEntity = newFlightEntity(pojoEntity.getKey(), "bos", "mia", "name");
1003      ds.put(FlightEntity);
1004
1005      Entity hasKeyPkEntity = new Entity(HasKeyPkJDO.class.getSimpleName(), pojoEntity.getKey());
1006      hasKeyPkEntity.setProperty("str", "yar");
1007      ds.put(hasKeyPkEntity);
1008
1009      Entity bidirEntity = new Entity(bidirClass.getSimpleName(), pojoEntity.getKey());
1010      bidirEntity.setProperty("childVal", "yap");
1011      ds.put(bidirEntity);
1012
1013      Transaction txn = EasyMock.createMock(Transaction.class);
1014      EasyMock.expect(txn.getId()).andReturn("1").times(2);
1015      txn.commit();
1016      EasyMock.expectLastCall();
1017      EasyMock.replay(txn);
1018      EasyMock.expect(mockDatastore.beginTransaction(EasyMock.isA(TransactionOptions.class))).andReturn(txn);
1019      // the only get we're going to perform is for the pojo
1020      EasyMock.expect(mockDatastore.get(txn, pojoEntity.getKey())).andReturn(pojoEntity);
1021      EasyMock.replay(mockDatastore);
1022
1023      beginTxn();
1024      HasOneToManyJDO pojo =
1025          pm.getObjectById(pojoClass, KeyFactory.keyToString(pojoEntity.getKey()));
1026      assertNotNull(pojo);
1027      pojo.getId();
1028      commitTxn();
1029    } finally {
1030      DatastoreServiceFactoryInternal.setDatastoreService(original);
1031    }
1032    EasyMock.verify(mockDatastore);
1033  }
1034
1035  void testDeleteParentDeletesChild(Class<? extends HasOneToManyJDO> pojoClass,
1036      Class<? extends BidirectionalChildJDO> bidirClass, StartEnd startEnd) throws Exception {
1037    Entity pojoEntity = new Entity(pojoClass.getSimpleName());
1038    ds.put(pojoEntity);
1039
1040    Entity flightEntity = newFlightEntity(pojoEntity.getKey(), "bos", "mia", "name");
1041    flightEntity.setProperty("flights_INTEGER_IDX", 1);
1042    ds.put(flightEntity);
1043
1044    Entity hasKeyPkEntity = new Entity(HasKeyPkJDO.class.getSimpleName(), pojoEntity.getKey());
1045    hasKeyPkEntity.setProperty("str", "yar");
1046    hasKeyPkEntity.setProperty("hasKeyPks_INTEGER_IDX", 1);
1047    ds.put(hasKeyPkEntity);
1048
1049    Entity bidirEntity = new Entity(bidirClass.getSimpleName(), pojoEntity.getKey());
1050    bidirEntity.setProperty("childVal", "yap");
1051    bidirEntity.setProperty("bidirChildren_INTEGER_IDX", 1);
1052    ds.put(bidirEntity);
1053
1054    pojoEntity.setProperty("bidirChildren", Utils.newArrayList(bidirEntity.getKey()));
1055    pojoEntity.setProperty("hasKeyPks", Utils.newArrayList(hasKeyPkEntity.getKey()));
1056    pojoEntity.setProperty("flights", Utils.newArrayList(flightEntity.getKey()));
1057    ds.put(pojoEntity);
1058
1059    startEnd.start();
1060    HasOneToManyJDO pojo = pm.getObjectById(pojoClass, KeyFactory.keyToString(pojoEntity.getKey()));
1061    pm.deletePersistent(pojo);
1062    startEnd.end();
1063    assertCountsInDatastore(pojoClass, bidirClass, 0, 0);
1064  }
1065
1066  void testRemoveAll(HasOneToManyJDO pojo, BidirectionalChildJDO bidir1,
1067                     BidirectionalChildJDO bidir2, BidirectionalChildJDO bidir3, StartEnd startEnd)
1068      throws EntityNotFoundException {
1069    Flight f1 = new Flight();
1070    Flight f2 = new Flight();
1071    Flight f3 = new Flight();
1072    pojo.addFlight(f1);
1073    pojo.addFlight(f2);
1074    pojo.addFlight(f3);
1075
1076    pojo.addBidirChild(bidir1);
1077    pojo.addBidirChild(bidir2);
1078    pojo.addBidirChild(bidir3);
1079
1080    startEnd.start();
1081    pm.makePersistent(pojo);
1082    startEnd.end();
1083    startEnd.start();
1084    pojo = pm.makePersistent(pojo);
1085    String f2Id = f2.getId();
1086    String bidir2Id = bidir2.getId();
1087    pojo.removeFlights(Collections.singleton(f2));
1088    pojo.removeBidirChildren(Collections.singleton(bidir2));
1089    startEnd.end();
1090    startEnd.start();
1091
1092    pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
1093
1094    assertEquals(2, pojo.getFlights().size());
1095    Set<String> flightIds = Utils.newHashSet(f1.getId(), f2.getId(), f3.getId());
1096    for (Flight f : pojo.getFlights()) {
1097      flightIds.remove(f.getId());
1098    }
1099    assertEquals(1, flightIds.size());
1100    assertEquals(f2.getId(), flightIds.iterator().next());
1101
1102    assertEquals(2, pojo.getBidirChildren().size());
1103    Set<String> bidirIds = Utils.newHashSet(bidir1.getId(), bidir2.getId(), bidir3.getId());
1104    for (BidirectionalChildJDO b : pojo.getBidirChildren()) {
1105      bidirIds.remove(b.getId());
1106    }
1107    assertEquals(1, bidirIds.size());
1108    assertEquals(bidir2.getId(), bidirIds.iterator().next());
1109    startEnd.end();
1110
1111    Entity flightEntity1 = ds.get(KeyFactory.stringToKey(f1.getId()));
1112    Entity flightEntity3 = ds.get(KeyFactory.stringToKey(f3.getId()));
1113    Entity bidirEntity1 = ds.get(KeyFactory.stringToKey(bidir1.getId()));
1114    Entity bidirEntity3 = ds.get(KeyFactory.stringToKey(bidir3.getId()));
1115    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
1116      assertEquals(0L, flightEntity1.getProperty("flights_INTEGER_IDX"));
1117      assertEquals(1L, flightEntity3.getProperty("flights_INTEGER_IDX"));
1118      assertEquals(0L, bidirEntity1.getProperty("bidirChildren_INTEGER_IDX"));
1119      assertEquals(1L, bidirEntity3.getProperty("bidirChildren_INTEGER_IDX"));
1120    }
1121    try {
1122      ds.get(KeyFactory.stringToKey(f2Id));
1123      fail("expected enfe");
1124    } catch (EntityNotFoundException enfe) {
1125      // good
1126    }
1127    try {
1128      ds.get(KeyFactory.stringToKey(bidir2Id));
1129      fail("expected enfe");
1130    } catch (EntityNotFoundException enfe) {
1131      // good
1132    }
1133  }
1134
1135  void testRemoveAll_LongPkOnParent(HasOneToManyLongPkJDO pojo, BidirectionalChildLongPkJDO bidir1,
1136                     BidirectionalChildLongPkJDO bidir2, BidirectionalChildLongPkJDO bidir3, StartEnd startEnd)
1137      throws EntityNotFoundException {
1138    Flight f1 = new Flight();
1139    Flight f2 = new Flight();
1140    Flight f3 = new Flight();
1141    pojo.addFlight(f1);
1142    pojo.addFlight(f2);
1143    pojo.addFlight(f3);
1144
1145    pojo.addBidirChild(bidir1);
1146    pojo.addBidirChild(bidir2);
1147    pojo.addBidirChild(bidir3);
1148
1149    startEnd.start();
1150    pm.makePersistent(pojo);
1151    startEnd.end();
1152    startEnd.start();
1153    pojo = pm.makePersistent(pojo);
1154    String f2Id = f2.getId();
1155    String bidir2Id = bidir2.getId();
1156    pojo.removeFlights(Collections.singleton(f2));
1157    pojo.removeBidirChildren(Collections.singleton(bidir2));
1158    startEnd.end();
1159    startEnd.start();
1160
1161    pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
1162    assertEquals(2, pojo.getFlights().size());
1163    Set<String> flightIds = Utils.newHashSet(f1.getId(), f2.getId(), f3.getId());
1164    for (Flight f : pojo.getFlights()) {
1165      flightIds.remove(f.getId());
1166    }
1167    assertEquals(1, flightIds.size());
1168    assertEquals(f2.getId(), flightIds.iterator().next());
1169
1170    assertEquals(2, pojo.getBidirChildren().size());
1171    Set<String> bidirIds = Utils.newHashSet(bidir1.getId(), bidir2.getId(), bidir3.getId());
1172    for (BidirectionalChildLongPkJDO b : pojo.getBidirChildren()) {
1173      bidirIds.remove(b.getId());
1174    }
1175    assertEquals(1, bidirIds.size());
1176    assertEquals(bidir2.getId(), bidirIds.iterator().next());
1177    startEnd.end();
1178
1179    Entity flightEntity1 = ds.get(KeyFactory.stringToKey(f1.getId()));
1180    Entity flightEntity3 = ds.get(KeyFactory.stringToKey(f3.getId()));
1181    Entity bidirEntity1 = ds.get(KeyFactory.stringToKey(bidir1.getId()));
1182    Entity bidirEntity3 = ds.get(KeyFactory.stringToKey(bidir3.getId()));
1183    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
1184      assertEquals(0L, flightEntity1.getProperty("flights_INTEGER_IDX_longpk"));
1185      assertEquals(1L, flightEntity3.getProperty("flights_INTEGER_IDX_longpk"));
1186      assertEquals(0L, bidirEntity1.getProperty("bidirChildren_INTEGER_IDX"));
1187      assertEquals(1L, bidirEntity3.getProperty("bidirChildren_INTEGER_IDX"));
1188    }
1189    try {
1190      ds.get(KeyFactory.stringToKey(f2Id));
1191      fail("expected enfe");
1192    } catch (EntityNotFoundException enfe) {
1193      // good
1194    }
1195    try {
1196      ds.get(KeyFactory.stringToKey(bidir2Id));
1197      fail("expected enfe");
1198    } catch (EntityNotFoundException enfe) {
1199      // good
1200    }
1201  }
1202
1203  void testRemoveAll_UnencodedStringPkOnParent(HasOneToManyUnencodedStringPkJDO pojo, BidirectionalChildUnencodedStringPkJDO bidir1,
1204                     BidirectionalChildUnencodedStringPkJDO bidir2, 
1205                     BidirectionalChildUnencodedStringPkJDO bidir3, StartEnd startEnd)
1206      throws EntityNotFoundException {
1207    Flight f1 = new Flight();
1208    Flight f2 = new Flight();
1209    Flight f3 = new Flight();
1210    pojo.addFlight(f1);
1211    pojo.addFlight(f2);
1212    pojo.addFlight(f3);
1213
1214    pojo.addBidirChild(bidir1);
1215    pojo.addBidirChild(bidir2);
1216    pojo.addBidirChild(bidir3);
1217
1218    startEnd.start();
1219    pm.makePersistent(pojo);
1220    startEnd.end();
1221    startEnd.start();
1222    pojo = pm.makePersistent(pojo);
1223    String f2Id = f2.getId();
1224    String bidir2Id = bidir2.getId();
1225    pojo.removeFlights(Collections.singleton(f2));
1226    pojo.removeBidirChildren(Collections.singleton(bidir2));
1227    startEnd.end();
1228    startEnd.start();
1229
1230    pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
1231
1232    assertEquals(2, pojo.getFlights().size());
1233    Set<String> flightIds = Utils.newHashSet(f1.getId(), f2.getId(), f3.getId());
1234    for (Flight f : pojo.getFlights()) {
1235      flightIds.remove(f.getId());
1236    }
1237    assertEquals(1, flightIds.size());
1238    assertEquals(f2.getId(), flightIds.iterator().next());
1239
1240    assertEquals(2, pojo.getBidirChildren().size());
1241    Set<String> bidirIds = Utils.newHashSet(bidir1.getId(), bidir2.getId(), bidir3.getId());
1242    for (BidirectionalChildUnencodedStringPkJDO b : pojo.getBidirChildren()) {
1243      bidirIds.remove(b.getId());
1244    }
1245    assertEquals(1, bidirIds.size());
1246    assertEquals(bidir2.getId(), bidirIds.iterator().next());
1247    startEnd.end();
1248
1249    Entity flightEntity1 = ds.get(KeyFactory.stringToKey(f1.getId()));
1250    Entity flightEntity3 = ds.get(KeyFactory.stringToKey(f3.getId()));
1251    Entity bidirEntity1 = ds.get(KeyFactory.stringToKey(bidir1.getId()));
1252    Entity bidirEntity3 = ds.get(KeyFactory.stringToKey(bidir3.getId()));
1253    if (isIndexed() && getStorageVersion(pm) == StorageVersion.PARENTS_DO_NOT_REFER_TO_CHILDREN) {
1254      assertEquals(0L, flightEntity1.getProperty("flights_INTEGER_IDX_unencodedstringpk"));
1255      assertEquals(1L, flightEntity3.getProperty("flights_INTEGER_IDX_unencodedstringpk"));
1256      assertEquals(0L, bidirEntity1.getProperty("bidirChildren_INTEGER_IDX"));
1257      assertEquals(1L, bidirEntity3.getProperty("bidirChildren_INTEGER_IDX"));
1258    }
1259    try {
1260      ds.get(KeyFactory.stringToKey(f2Id));
1261      fail("expected enfe");
1262    } catch (EntityNotFoundException enfe) {
1263      // good
1264    }
1265    try {
1266      ds.get(KeyFactory.stringToKey(bidir2Id));
1267      fail("expected enfe");
1268    } catch (EntityNotFoundException enfe) {
1269      // good
1270    }
1271  }
1272
1273  void testChangeParent(HasOneToManyJDO pojo, HasOneToManyJDO pojo2, StartEnd startEnd) {
1274    switchDatasource(PersistenceManagerFactoryName.nontransactional);
1275    Flight f1 = new Flight();
1276    pojo.addFlight(f1);
1277
1278    startEnd.start();
1279    pm.makePersistent(pojo);
1280    startEnd.end();
1281
1282    startEnd.start();
1283    pojo2.addFlight(f1);
1284    try {
1285      pm.makePersistent(pojo2);
1286      fail("expected exception");
1287    } catch (JDOFatalUserException e) {
1288      if (pm.currentTransaction().isActive()) {
1289        rollbackTxn();
1290      }
1291    }
1292  }
1293
1294  void testNewParentNewChild_NamedKeyOnChild(HasOneToManyJDO pojo, StartEnd startEnd) throws EntityNotFoundException {
1295    Flight f1 = new Flight();
1296    pojo.addFlight(f1);
1297    f1.setId(KeyFactory.keyToString(
1298        KeyFactory.createKey(Flight.class.getSimpleName(), "named key")));
1299    startEnd.start();
1300    pm.makePersistent(pojo);
1301    startEnd.end();
1302
1303    Entity flightEntity = ds.get(KeyFactory.stringToKey(f1.getId()));
1304    assertEquals("named key", flightEntity.getKey().getName());
1305  }
1306
1307  void testAddAlreadyPersistedChildToParent_NoTxnDifferentPm(HasOneToManyJDO pojo) {
1308    switchDatasource(PersistenceManagerFactoryName.nontransactional);
1309    Flight f1 = new Flight();
1310    pm.makePersistent(f1);
1311    f1 = pm.detachCopy(f1);
1312    pm.close();
1313    pm = pmf.getPersistenceManager();
1314    f1 = pm.makePersistent(f1);
1315    pojo.addFlight(f1);
1316    try {
1317      pm.makePersistent(pojo);
1318      fail("expected exception");
1319    } catch (JDOFatalUserException e) {
1320      // good
1321    }
1322    pm.close();
1323
1324    assertEquals(0, countForClass(pojo.getClass()));
1325    assertEquals(1, countForClass(Flight.class));
1326  }
1327
1328  void testAddAlreadyPersistedChildToParent_NoTxnSamePm(HasOneToManyJDO pojo) {
1329    switchDatasource(PersistenceManagerFactoryName.nontransactional);
1330    Flight f1 = new Flight();
1331    pm.makePersistent(f1);
1332    pojo.addFlight(f1);
1333    try {
1334      pm.makePersistent(pojo);
1335      fail("expected exception");
1336    } catch (JDOFatalUserException e) {
1337      // good
1338    }
1339    pm.close();
1340
1341    assertEquals(0, countForClass(pojo.getClass()));
1342    assertEquals(1, countForClass(Flight.class));
1343  }
1344
1345  void testFetchOfOneToManyParentWithKeyPk(HasOneToManyKeyPkJDO pojo, StartEnd startEnd) {
1346    startEnd.start();
1347    pm.makePersistent(pojo);
1348    startEnd.end();
1349
1350    startEnd.start();
1351    pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
1352    assertEquals(0, pojo.getFlights().size());
1353    startEnd.end();
1354  }
1355
1356  void testFetchOfOneToManyParentWithLongPk(HasOneToManyLongPkJDO pojo, StartEnd startEnd) {
1357    startEnd.start();
1358    pm.makePersistent(pojo);
1359    startEnd.end();
1360
1361    startEnd.start();
1362    pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
1363    assertEquals(0, pojo.getFlights().size());
1364    startEnd.end();
1365  }
1366
1367  void testFetchOfOneToManyParentWithUnencodedStringPk(HasOneToManyUnencodedStringPkJDO pojo, StartEnd startEnd) {
1368    pojo.setId("yar");
1369    startEnd.start();
1370    pm.makePersistent(pojo);
1371    startEnd.end();
1372
1373    startEnd.start();
1374    pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
1375    assertEquals(0, pojo.getFlights().size());
1376    startEnd.end();
1377  }
1378
1379  void testAddChildToOneToManyParentWithLongPk(
1380      HasOneToManyLongPkJDO pojo, BidirectionalChildLongPkJDO bidirChild, StartEnd startEnd)
1381      throws EntityNotFoundException {
1382    startEnd.start();
1383    pm.makePersistent(pojo);
1384    startEnd.end();
1385
1386    startEnd.start();
1387    pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
1388    Flight f = new Flight();
1389    pojo.addFlight(f);
1390    pojo.addBidirChild(bidirChild);
1391    startEnd.end();
1392    Entity flightEntity = ds.get(KeyFactory.stringToKey(f.getId()));
1393    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidirChild.getId()));
1394    Entity pojoEntity = ds.get(KeyFactory.createKey(pojo.getClass().getSimpleName(), pojo.getId()));
1395    assertEquals(pojoEntity.getKey(), flightEntity.getParent());
1396    assertEquals(pojoEntity.getKey(), bidirEntity.getParent());
1397  }
1398
1399  void testAddChildToOneToManyParentWithUnencodedStringPk(
1400      HasOneToManyUnencodedStringPkJDO pojo, BidirectionalChildUnencodedStringPkJDO bidirChild, 
1401      StartEnd startEnd)
1402      throws EntityNotFoundException {
1403    pojo.setId("yar");
1404    startEnd.start();
1405    pm.makePersistent(pojo);
1406    startEnd.end();
1407
1408    startEnd.start();
1409    pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
1410    Flight f = new Flight();
1411    pojo.addFlight(f);
1412    pojo.addBidirChild(bidirChild);
1413    startEnd.end();
1414    Entity flightEntity = ds.get(KeyFactory.stringToKey(f.getId()));
1415    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidirChild.getId()));
1416    Entity pojoEntity = ds.get(KeyFactory.createKey(pojo.getClass().getSimpleName(), pojo.getId()));
1417    assertEquals(pojoEntity.getKey(), flightEntity.getParent());
1418    assertEquals(pojoEntity.getKey(), bidirEntity.getParent());
1419  }
1420
1421  void testAddQueriedParentToBidirChild(HasOneToManyJDO pojo, BidirectionalChildJDO bidir, StartEnd startEnd)
1422      throws EntityNotFoundException {
1423    startEnd.start();
1424    pm.makePersistent(pojo);
1425    startEnd.end();
1426
1427    startEnd.start();
1428    pojo = (HasOneToManyJDO) ((List<?>)pm.newQuery(pojo.getClass()).execute()).get(0);
1429    bidir.setParent(pojo);
1430    pojo.addBidirChild(bidir);
1431    pm.makePersistent(bidir);
1432    startEnd.end();
1433
1434    assertEquals(1, countForClass(bidir.getClass()));
1435    Entity e = ds.prepare(new Query(bidir.getClass().getSimpleName())).asSingleEntity();
1436    assertNotNull(e.getParent());
1437
1438    startEnd.start();
1439    pojo = (HasOneToManyJDO) ((List<?>)pm.newQuery(pojo.getClass()).execute()).get(0);
1440    assertEquals(1, pojo.getBidirChildren().size());
1441    startEnd.end();
1442    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidir.getId()));
1443    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
1444    assertEquals(pojoEntity.getKey(), bidirEntity.getParent());
1445  }
1446
1447  void testAddFetchedParentToBidirChild(HasOneToManyJDO pojo, BidirectionalChildJDO bidir, StartEnd startEnd)
1448      throws EntityNotFoundException {
1449    startEnd.start();
1450    pm.makePersistent(pojo);
1451    startEnd.end();
1452
1453    startEnd.start();
1454    pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
1455    bidir.setParent(pojo);
1456    pojo.addBidirChild(bidir);
1457    pm.makePersistent(bidir);
1458    startEnd.end();
1459
1460    assertEquals(1, countForClass(bidir.getClass()));
1461    Entity e = ds.prepare(new Query(bidir.getClass().getSimpleName())).asSingleEntity();
1462    assertNotNull(e.getParent());
1463    startEnd.start();
1464    pojo = (HasOneToManyJDO) ((List<?>)pm.newQuery(pojo.getClass()).execute()).get(0);
1465    assertEquals(1, pojo.getBidirChildren().size());
1466    startEnd.end();
1467    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidir.getId()));
1468    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
1469    assertEquals(pojoEntity.getKey(), bidirEntity.getParent());
1470  }
1471
1472  void testReplaceBidirColl(HasOneToManyJDO parent,
1473                            BidirectionalChildJDO bidir1,
1474                            Collection<BidirectionalChildJDO> newColl, StartEnd startEnd) {
1475    bidir1.setParent(parent);
1476    parent.addBidirChild(bidir1);
1477
1478    startEnd.start();
1479    pm.makePersistent(parent);
1480    startEnd.end();
1481    String childKey = bidir1.getId();
1482    startEnd.start();
1483    parent = pm.getObjectById(parent.getClass(), KeyFactory.stringToKey(parent.getId()));
1484    parent.setBidirChildren(newColl);
1485    startEnd.end();
1486
1487    startEnd.start();
1488    parent = pm.getObjectById(parent.getClass(), KeyFactory.stringToKey(parent.getId()));
1489    assertEquals(2, parent.getBidirChildren().size());
1490    Iterator<BidirectionalChildJDO> childIter = parent.getBidirChildren().iterator();
1491    assertFalse(childKey.equals(childIter.next().getId()));
1492    assertFalse(childKey.equals(childIter.next().getId()));
1493    startEnd.end();
1494    assertEquals(2, countForClass(newColl.iterator().next().getClass()));
1495  }
1496
1497  private static final class PutPolicy implements DatastoreServiceInterceptor.Policy {
1498    private final List<Object[]> putParamList = Utils.newArrayList();
1499    public void intercept(Object o, Method method, Object[] params) {
1500      if (method.getName().equals("put")) {
1501        putParamList.add(params);
1502      }
1503    }
1504  }
1505
1506  PutPolicy setupPutPolicy(HasOneToManyJDO pojo, BidirectionalChildJDO bidir, StartEnd startEnd)
1507      throws Throwable {
1508    PutPolicy policy = new PutPolicy();
1509    DatastoreServiceInterceptor.install(getStoreManager(), policy);
1510    try {
1511      pmf.close();
1512      switchDatasource(startEnd.getPmfName());
1513      Flight flight = new Flight();
1514      pojo.addFlight(flight);
1515      pojo.addBidirChild(bidir);
1516      HasKeyPkJDO hasKeyPk = new HasKeyPkJDO();
1517      pojo.addHasKeyPk(hasKeyPk);
1518
1519      startEnd.start();
1520      pm.makePersistent(pojo);
1521      startEnd.end();
1522      // 1 put for the parent, 3 puts for the children, 1 more put
1523      // to add the child keys back on the parent
1524      assertEquals(5, policy.putParamList.size());
1525      policy.putParamList.clear();
1526      return policy;
1527    } catch (Throwable t) {
1528      DatastoreServiceInterceptor.uninstall();
1529      throw t;
1530    }
1531  }
1532
1533  void testOnlyOnePutOnChildUpdate(HasOneToManyJDO pojo, BidirectionalChildJDO bidir, StartEnd startEnd)
1534      throws Throwable {
1535    PutPolicy policy = setupPutPolicy(pojo, bidir, startEnd);
1536    try {
1537      startEnd.start();
1538      pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
1539      pojo.getFlights().iterator().next().setMe(88);
1540      pojo.getBidirChildren().iterator().next().setChildVal("blarg");
1541      pojo.getHasKeyPks().iterator().next().setStr("double blarg");
1542      startEnd.end();
1543    } finally {
1544      DatastoreServiceInterceptor.uninstall();
1545    }
1546    // 1 put for each child update
1547    assertEquals(3, policy.putParamList.size());
1548  }
1549
1550  void testOnlyOneParentPutOnParentAndChildUpdate(HasOneToManyJDO pojo, BidirectionalChildJDO bidir, StartEnd startEnd)
1551      throws Throwable {
1552    PutPolicy policy = setupPutPolicy(pojo, bidir, startEnd);
1553    try {
1554      startEnd.start();
1555      pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
1556      pojo.setVal("another val");
1557      pojo.getFlights().iterator().next().setMe(88);
1558      pojo.getBidirChildren().iterator().next().setChildVal("blarg");
1559      pojo.getHasKeyPks().iterator().next().setStr("double blarg");
1560      startEnd.end();
1561    } finally {
1562      DatastoreServiceInterceptor.uninstall();
1563    }
1564    // 1 put for the parent update, 1 put for each child update
1565    assertEquals(4, policy.putParamList.size());
1566  }
1567
1568  void testOnlyOneParentPutOnChildDelete(HasOneToManyJDO pojo, BidirectionalChildJDO bidir, StartEnd startEnd)
1569      throws Throwable {
1570    PutPolicy policy = setupPutPolicy(pojo, bidir, startEnd);
1571    try {
1572      startEnd.start();
1573      pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
1574      pojo.setVal("another val");
1575      pojo.nullFlights();
1576      pojo.nullBidirChildren();
1577      pojo.nullHasKeyPks();
1578      startEnd.end();
1579    } finally {
1580      DatastoreServiceInterceptor.uninstall();
1581    }
1582    if (startEnd == TXN_START_END) {
1583      // transactional, so 1 put to do all
1584      assertEquals(1, policy.putParamList.size());
1585    } else {
1586      // non-tx so 1 for each
1587      assertEquals(4, policy.putParamList.size());
1588    }
1589  }
1590
1591  void testNonTxnAddOfChildToParentFailsPartwayThrough(HasOneToManyJDO pojo)
1592      throws Throwable {
1593    Flight flight1 = new Flight();
1594    pojo.addFlight(flight1);
1595    beginTxn();
1596    pm.makePersistent(pojo);
1597    commitTxn();
1598    final String kind = kindForObject(pojo);
1599    DatastoreServiceInterceptor.Policy policy = new DatastoreServiceInterceptor.Policy() {
1600      public void intercept(Object o, Method method, Object[] params) {
1601        if (method.getName().equals("put") && ((Entity) params[0]).getKind().equals(kind)) {
1602          throw new ConcurrentModificationException("kaboom");
1603        }
1604      }
1605    };
1606    DatastoreServiceInterceptor.install(getStoreManager(), policy);
1607    Flight flight2 = new Flight();
1608    try {
1609      pmf.close();
1610      switchDatasource(PersistenceManagerFactoryName.nontransactional);
1611      pojo = pm.getObjectById(pojo.getClass(), pojo.getId());
1612      pojo.addFlight(flight2);
1613      pm.close();
1614      fail("expected exception");
1615    } catch (ConcurrentModificationException cme) {
1616      // good
1617    } finally {
1618      DatastoreServiceInterceptor.uninstall();
1619    }
1620    // prove that the book entity exists
1621    ds.get(KeyFactory.stringToKey(flight2.getId()));
1622    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
1623    // the parent has a reference to the first book that was already there
1624    // but no reference to the second book
1625    assertEquals(
1626        Collections.singletonList(KeyFactory.stringToKey(flight1.getId())),
1627        pojoEntity.getProperty("flights"));
1628  }
1629
1630
1631  void testSaveWithOrderBy(HasOneToManyListWithOrderByJDO pojo, StartEnd startEnd)
1632      throws EntityNotFoundException {
1633    getExecutionContext().getNucleusContext().getPersistenceConfiguration().setProperty(
1634        "datanucleus.appengine.allowMultipleRelationsOfSameType", true);
1635    startEnd.start();
1636    pm.makePersistent(pojo);
1637    startEnd.end();
1638  }
1639
1640//  void testIndexOf(HasOneToManyJDO pojo, BidirectionalChildJDO bidir1, BidirectionalChildJDO bidir2,
1641//                   BidirectionalChildJDO bidir3) {
1642//
1643//    Flight f1 = new Flight();
1644//    Flight f2 = new Flight();
1645//    Flight f3 = new Flight();
1646//    pojo.addFlight(f1);
1647//    pojo.addFlight(f2);
1648//    pojo.addFlight(f3);
1649//
1650//    pojo.addBidirChild(bidir1);
1651//    pojo.addBidirChild(bidir2);
1652//    pojo.addBidirChild(bidir3);
1653//
1654//    startEnd.start();
1655//    pm.makePersistent(pojo);
1656//    startEnd.end();
1657//    startEnd.start();
1658//    assertEquals(0, pojo.indexOf(f1));
1659//    assertEquals(1, pojo.indexOf(f2));
1660//    assertEquals(2, pojo.indexOf(f3));
1661//    assertEquals(0, pojo.indexOf(bidir1));
1662//    assertEquals(1, pojo.indexOf(bidir2));
1663//    assertEquals(2, pojo.indexOf(bidir3));
1664//    startEnd.end();
1665//
1666//    startEnd.start();
1667//    pojo = pm.getObjectById(pojo.getClass(), pojo.getKey());
1668//    assertEquals(0, pojo.indexOf(f1));
1669//    assertEquals(1, pojo.indexOf(f2));
1670//    assertEquals(2, pojo.indexOf(f3));
1671//    assertEquals(0, pojo.indexOf(bidir1));
1672//    assertEquals(1, pojo.indexOf(bidir2));
1673//    assertEquals(2, pojo.indexOf(bidir3));
1674//    startEnd.end();
1675//  }
1676
1677  void assertCountsInDatastore(Class<? extends HasOneToManyJDO> parentClass,
1678      Class<? extends BidirectionalChildJDO> bidirClass,
1679      int expectedParent, int expectedChildren) {
1680    assertEquals(parentClass.getName(), expectedParent, countForClass(parentClass));
1681    assertEquals(bidirClass.getName(), expectedChildren, countForClass(bidirClass));
1682    assertEquals(
1683        Flight.class.getName(), expectedChildren, countForClass(Flight.class));
1684    assertEquals(
1685        HasKeyPkJDO.class.getName(), expectedChildren, countForClass(HasKeyPkJDO.class));
1686  }
1687
1688  Flight newFlight() {
1689    Flight f = new Flight();
1690    f.setOrigin("bos");
1691    f.setDest("mia");
1692    f.setName("jimmy");
1693    f.setMe(22);
1694    f.setYou(26);
1695    f.setFlightNumber(99);
1696    return f;
1697  }
1698
1699  Entity newFlightEntity(
1700      Key parentKey, String orig, String dest, String name) {
1701    Entity entity = new Entity(Flight.class.getSimpleName(), parentKey);
1702    entity.setProperty("origin", orig);
1703    entity.setProperty("dest", dest);
1704    entity.setProperty("name", name);
1705    entity.setProperty("you", 44);
1706    entity.setProperty("me", 45);
1707    entity.setProperty("flight_number", 99);
1708    entity.setProperty("flights_INTEGER_IDX", 1);
1709    return entity;
1710  }
1711
1712  abstract boolean isIndexed();
1713}