PageRenderTime 221ms CodeModel.GetById 141ms app.highlight 72ms RepoModel.GetById 1ms app.codeStats 0ms

/tests/com/google/appengine/datanucleus/jpa/JPAOneToManyPolymorphicTestCase.java

http://datanucleus-appengine.googlecode.com/
Java | 1130 lines | 929 code | 168 blank | 33 comment | 8 complexity | d6dfc2009fc3937026297f213463edbf MD5 | raw file
   1/**********************************************************************
   2Copyright (c) 2011 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.jpa;
  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.Utils;
  30import com.google.appengine.datanucleus.Utils.Function;
  31import com.google.appengine.datanucleus.test.jpa.HasKeyPkJPA;
  32import com.google.appengine.datanucleus.test.jpa.HasPolymorphicRelationsListJPA;
  33import com.google.appengine.datanucleus.test.jpa.UnidirectionalSingeTableChildJPA;
  34import com.google.appengine.datanucleus.test.jpa.BidirectionalSingleTableChildJPA.BidirTop;
  35import com.google.appengine.datanucleus.test.jpa.BidirectionalSingleTableChildJPA.BidirTopLongPk;
  36import com.google.appengine.datanucleus.test.jpa.BidirectionalSingleTableChildJPA.BidirTopUnencodedStringPk;
  37import com.google.appengine.datanucleus.test.jpa.HasPolymorphicRelationsJPA.HasOneToManyJPA;
  38import com.google.appengine.datanucleus.test.jpa.HasPolymorphicRelationsJPA.HasOneToManyKeyPkJPA;
  39import com.google.appengine.datanucleus.test.jpa.HasPolymorphicRelationsJPA.HasOneToManyLongPkJPA;
  40import com.google.appengine.datanucleus.test.jpa.HasPolymorphicRelationsJPA.HasOneToManyUnencodedStringPkJPA;
  41import com.google.appengine.datanucleus.test.jpa.UnidirectionalSingeTableChildJPA.UnidirBottom;
  42import com.google.appengine.datanucleus.test.jpa.UnidirectionalSingeTableChildJPA.UnidirMiddle;
  43import com.google.appengine.datanucleus.test.jpa.UnidirectionalSingeTableChildJPA.UnidirTop;
  44
  45import org.easymock.EasyMock;
  46
  47import java.lang.reflect.Method;
  48import java.util.Collections;
  49import java.util.List;
  50
  51import javax.persistence.PersistenceException;
  52
  53import static com.google.appengine.datanucleus.PolymorphicTestUtils.getEntityKind;
  54import static com.google.appengine.datanucleus.TestUtils.assertKeyParentEquals;
  55
  56abstract class JPAOneToManyPolymorphicTestCase extends JPATestCase {
  57  
  58  public enum UnidirLevel {
  59    Top(UnidirectionalSingeTableChildJPA.DISCRIMINATOR_TOP, UnidirTop.class),
  60    Middle(UnidirectionalSingeTableChildJPA.DISCRIMINATOR_MIDDLE, UnidirMiddle.class),
  61    Bottom(UnidirectionalSingeTableChildJPA.DISCRIMINATOR_BOTTOM, UnidirBottom.class);
  62
  63    final String discriminator;
  64    final Class<?> clazz;
  65    
  66    UnidirLevel(String discriminator,  Class<?> clazz) {
  67      this.discriminator = discriminator;
  68      this.clazz = clazz;
  69    }
  70    
  71  }
  72
  73  void testInsert_NewParentAndChild(BidirTop bidirChild, HasOneToManyJPA parent,
  74                                    StartEnd startEnd, UnidirLevel unidirLevel,
  75                                    int expectedParent, int expectedChildren)
  76      throws Exception {
  77    bidirChild.setChildVal("yam");
  78
  79    UnidirTop unidir = newUnidir(unidirLevel);
  80    String expectedName = unidir.getName();
  81    String expectedStr = unidir.getStr();
  82
  83    HasKeyPkJPA hasKeyPk = new HasKeyPkJPA();
  84    hasKeyPk.setStr("yag");
  85
  86    parent.getBidirChildren().add(bidirChild);
  87    bidirChild.setParent(parent);
  88    parent.getUnidirChildren().add(unidir);
  89    parent.getHasKeyPks().add(hasKeyPk);
  90    parent.setVal("yar");
  91
  92    startEnd.start();
  93    em.persist(parent);
  94    startEnd.end();
  95
  96    assertNotNull(bidirChild.getId());
  97    assertNotNull(unidir.getId());
  98    assertNotNull(hasKeyPk.getId());
  99
 100    Entity bidirChildEntity = ds.get(KeyFactory.stringToKey(bidirChild.getId()));
 101    assertNotNull(bidirChildEntity);
 102    assertEquals(bidirChild.getClass().getName(), bidirChildEntity.getProperty("DTYPE"));
 103    assertEquals(bidirChild.getPropertyCount(), bidirChildEntity.getProperties().size());
 104    assertEquals("yam", bidirChildEntity.getProperty("childVal"));
 105    assertEquals(KeyFactory.stringToKey(bidirChild.getId()), bidirChildEntity.getKey());
 106    assertKeyParentEquals(parent.getId(), bidirChildEntity, bidirChild.getId());
 107
 108    Entity unidirEntity = ds.get(KeyFactory.stringToKey(unidir.getId()));
 109    assertNotNull(unidirEntity);
 110    assertEquals(unidirLevel.discriminator, unidirEntity.getProperty("DTYPE"));
 111    assertEquals(expectedName, unidirEntity.getProperty("name"));
 112    assertEquals(expectedStr, unidirEntity.getProperty("str"));
 113    assertEquals(KeyFactory.stringToKey(unidir.getId()), unidirEntity.getKey());
 114    assertKeyParentEquals(parent.getId(), unidirEntity, unidir.getId());
 115
 116    Entity hasKeyPkEntity = ds.get(hasKeyPk.getId());
 117    assertNotNull(hasKeyPkEntity);
 118    assertEquals("yag", hasKeyPkEntity.getProperty("str"));
 119    assertEquals(hasKeyPk.getId(), hasKeyPkEntity.getKey());
 120    assertKeyParentEquals(parent.getId(), hasKeyPkEntity, hasKeyPk.getId());
 121
 122    Entity parentEntity = ds.get(KeyFactory.stringToKey(parent.getId()));
 123    assertNotNull(parentEntity);
 124    assertEquals(4, parentEntity.getProperties().size());
 125    assertEquals("yar", parentEntity.getProperty("val"));
 126    assertEquals(Collections.singletonList(bidirChildEntity.getKey()), parentEntity.getProperty("bidirChildren"));
 127    assertEquals(Collections.singletonList(unidirEntity.getKey()), parentEntity.getProperty("unidirChildren"));
 128    assertEquals(Collections.singletonList(hasKeyPkEntity.getKey()), parentEntity.getProperty("hasKeyPks"));
 129
 130    assertCountsInDatastore(parent.getClass(), bidirChild.getClass(), expectedParent, expectedChildren);
 131  }
 132
 133  void testInsert_ExistingParentNewChild(BidirTop bidirChild, HasOneToManyJPA pojo,
 134                                         StartEnd startEnd, UnidirLevel unidirLevel,
 135                                         int expectedParent, int expectedChildren) throws Exception {
 136    pojo.setVal("yar");
 137
 138    startEnd.start();
 139    em.persist(pojo);
 140    startEnd.end();
 141    assertNotNull(pojo.getId());
 142    assertTrue(pojo.getUnidirChildren().isEmpty());
 143    assertTrue(pojo.getHasKeyPks().isEmpty());
 144    assertTrue(pojo.getBidirChildren().isEmpty());
 145
 146    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 147    assertNotNull(pojoEntity);
 148    assertEquals(4, pojoEntity.getProperties().size());
 149    assertTrue(pojoEntity.hasProperty("bidirChildren"));
 150    assertNull(pojoEntity.getProperty("bidirChildren"));
 151    assertTrue(pojoEntity.hasProperty("unidirChildren"));
 152    assertNull(pojoEntity.getProperty("unidirChildren"));
 153    assertTrue(pojoEntity.hasProperty("hasKeyPks"));
 154    assertNull(pojoEntity.getProperty("hasKeyPks"));
 155
 156    startEnd.start();
 157    UnidirTop unidir = newUnidir(unidirLevel);
 158    String expectedName = unidir.getName();
 159    String expectedStr = unidir.getStr();
 160    pojo.getUnidirChildren().add(unidir);
 161
 162    HasKeyPkJPA hasKeyPk = new HasKeyPkJPA();
 163    hasKeyPk.setStr("yag");
 164    pojo.getHasKeyPks().add(hasKeyPk);
 165
 166    bidirChild.setChildVal("yam");
 167    pojo.getBidirChildren().add(bidirChild);
 168    bidirChild.setParent(pojo);
 169
 170    em.merge(pojo);
 171    startEnd.end();
 172
 173    assertNotNull(bidirChild.getId());
 174    assertNotNull(bidirChild.getParent());
 175    assertNotNull(unidir.getId());
 176    assertNotNull(hasKeyPk.getId());
 177
 178    Entity bidirChildEntity = ds.get(KeyFactory.stringToKey(bidirChild.getId()));
 179    assertNotNull(bidirChildEntity);
 180    assertEquals("yam", bidirChildEntity.getProperty("childVal"));
 181    assertEquals(KeyFactory.stringToKey(bidirChild.getId()), bidirChildEntity.getKey());
 182    assertKeyParentEquals(pojo.getId(), bidirChildEntity, bidirChild.getId());
 183
 184    Entity unidirEntity = ds.get(KeyFactory.stringToKey(unidir.getId()));
 185    assertNotNull(unidirEntity);
 186    assertEquals(expectedName, unidirEntity.getProperty("name"));
 187    assertEquals(expectedStr, unidirEntity.getProperty("str"));
 188    assertEquals(KeyFactory.stringToKey(unidir.getId()), unidirEntity.getKey());
 189    assertKeyParentEquals(pojo.getId(), unidirEntity, unidir.getId());
 190
 191    Entity hasKeyPkEntity = ds.get(hasKeyPk.getId());
 192    assertNotNull(hasKeyPkEntity);
 193    assertEquals("yag", hasKeyPkEntity.getProperty("str"));
 194    assertEquals(hasKeyPk.getId(), hasKeyPkEntity.getKey());
 195    assertKeyParentEquals(pojo.getId(), hasKeyPkEntity, hasKeyPk.getId());
 196
 197    Entity parentEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 198    assertNotNull(parentEntity);
 199    assertEquals(4, parentEntity.getProperties().size());
 200    assertEquals("yar", parentEntity.getProperty("val"));
 201    assertEquals(Collections.singletonList(bidirChildEntity.getKey()), parentEntity.getProperty("bidirChildren"));
 202    assertEquals(Collections.singletonList(unidirEntity.getKey()), parentEntity.getProperty("unidirChildren"));
 203    assertEquals(Collections.singletonList(hasKeyPkEntity.getKey()), parentEntity.getProperty("hasKeyPks"));
 204
 205    assertCountsInDatastore(pojo.getClass(), bidirChild.getClass(), expectedParent, expectedChildren);
 206  }
 207
 208  void testUpdate_UpdateChildWithMerge(BidirTop bidir, HasOneToManyJPA pojo,
 209      StartEnd startEnd, UnidirLevel unidirLevel, int expectedParent, int expectedChildren) throws Exception {
 210    UnidirTop unidir = newUnidir(unidirLevel);
 211    HasKeyPkJPA hasKeyPk = new HasKeyPkJPA();
 212
 213    pojo.getUnidirChildren().add(unidir);
 214    pojo.getHasKeyPks().add(hasKeyPk);
 215    pojo.getBidirChildren().add(bidir);
 216    bidir.setParent(pojo);
 217
 218    startEnd.start();
 219    em.persist(pojo);
 220    startEnd.end();
 221
 222    assertNotNull(unidir.getId());
 223    assertNotNull(hasKeyPk.getId());
 224    assertNotNull(bidir.getId());
 225    assertNotNull(pojo.getId());
 226
 227    startEnd.start();
 228    unidir.setName("yam");
 229    hasKeyPk.setStr("yar");
 230    bidir.setChildVal("yap");
 231    em.merge(pojo);
 232    startEnd.end();
 233
 234    Entity unidirEntity = ds.get(KeyFactory.stringToKey(unidir.getId()));
 235    assertNotNull(unidirEntity);
 236    assertEquals("yam", unidirEntity.getProperty("name"));
 237    assertKeyParentEquals(pojo.getId(), unidirEntity, unidir.getId());
 238
 239    Entity hasKeyPkEntity = ds.get(hasKeyPk.getId());
 240    assertNotNull(hasKeyPkEntity);
 241    assertEquals("yar", hasKeyPkEntity.getProperty("str"));
 242    assertKeyParentEquals(pojo.getId(), hasKeyPkEntity, hasKeyPk.getId());
 243
 244    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidir.getId()));
 245    assertNotNull(bidirEntity);
 246    assertEquals("yap", bidirEntity.getProperty("childVal"));
 247    assertKeyParentEquals(pojo.getId(), bidirEntity, bidir.getId());
 248
 249    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), expectedParent, expectedChildren);
 250  }
 251
 252  void testUpdate_UpdateChild(BidirTop bidir, HasOneToManyJPA pojo,
 253                              StartEnd startEnd, UnidirLevel unidirLevel,
 254                              int expectedParent, int expectedChildren) throws Exception {
 255    UnidirTop unidir = newUnidir(unidirLevel);
 256    HasKeyPkJPA hasKeyPk = new HasKeyPkJPA();
 257
 258    pojo.getUnidirChildren().add(unidir);
 259    pojo.getHasKeyPks().add(hasKeyPk);
 260    pojo.getBidirChildren().add(bidir);
 261    bidir.setParent(pojo);
 262
 263    startEnd.start();
 264    em.persist(pojo);
 265    startEnd.end();
 266
 267    assertNotNull(unidir.getId());
 268    assertNotNull(hasKeyPk.getId());
 269    assertNotNull(bidir.getId());
 270    assertNotNull(pojo.getId());
 271
 272    startEnd.start();
 273    pojo = em.find(pojo.getClass(), pojo.getId());
 274    pojo.getUnidirChildren().iterator().next().setName("yam");
 275    pojo.getHasKeyPks().iterator().next().setStr("yar");
 276    pojo.getBidirChildren().iterator().next().setChildVal("yap");
 277    startEnd.end();
 278
 279    Entity unidirEntity = ds.get(KeyFactory.stringToKey(unidir.getId()));
 280    assertNotNull(unidirEntity);
 281    assertEquals("yam", unidirEntity.getProperty("name"));
 282    assertKeyParentEquals(pojo.getId(), unidirEntity, unidir.getId());
 283
 284    Entity hasKeyPkEntity = ds.get(hasKeyPk.getId());
 285    assertNotNull(hasKeyPkEntity);
 286    assertEquals("yar", hasKeyPkEntity.getProperty("str"));
 287    assertKeyParentEquals(pojo.getId(), hasKeyPkEntity, hasKeyPk.getId());
 288
 289    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidir.getId()));
 290    assertNotNull(bidirEntity);
 291    assertEquals("yap", bidirEntity.getProperty("childVal"));
 292    assertKeyParentEquals(pojo.getId(), bidirEntity, bidir.getId());
 293
 294    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), expectedParent, expectedChildren);
 295  }
 296
 297  void testUpdate_NullOutChildren(BidirTop bidir, HasOneToManyJPA pojo,
 298                                  StartEnd startEnd, UnidirLevel unidirLevel,
 299                                  int expectedParent) throws Exception {
 300    UnidirTop unidir = newUnidir(unidirLevel);
 301    HasKeyPkJPA hasKeyPk = new HasKeyPkJPA();
 302
 303    pojo.getUnidirChildren().add(unidir);
 304    pojo.getHasKeyPks().add(hasKeyPk);
 305    pojo.getBidirChildren().add(bidir);
 306    bidir.setParent(pojo);
 307
 308    startEnd.start();
 309    em.persist(pojo);
 310    startEnd.end();
 311
 312    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), expectedParent, 1);
 313
 314    startEnd.start();
 315    pojo.nullUnidirChildren();
 316    pojo.nullHasKeyPks();
 317    pojo.nullBidirChildren();
 318    em.merge(pojo);
 319    startEnd.end();
 320
 321    try {
 322      ds.get(KeyFactory.stringToKey(unidir.getId()));
 323      fail("expected enfe");
 324    } catch (EntityNotFoundException enfe) {
 325      // good
 326    }
 327
 328    try {
 329      ds.get(hasKeyPk.getId());
 330      fail("expected enfe");
 331    } catch (EntityNotFoundException enfe) {
 332      // good
 333    }
 334
 335    try {
 336      ds.get(KeyFactory.stringToKey(bidir.getId()));
 337      fail("expected enfe");
 338    } catch (EntityNotFoundException enfe) {
 339      // good
 340    }
 341
 342    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 343    assertEquals(4, pojoEntity.getProperties().size());
 344    assertTrue(pojoEntity.hasProperty("bidirChildren"));
 345    assertNull(pojoEntity.getProperty("bidirChildren"));
 346    assertTrue(pojoEntity.hasProperty("unidirChildren"));
 347    assertNull(pojoEntity.getProperty("unidirChildren"));
 348    assertTrue(pojoEntity.hasProperty("hasKeyPks"));
 349    assertNull(pojoEntity.getProperty("hasKeyPks"));
 350
 351    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), expectedParent, 0);
 352  }
 353
 354  void testUpdate_ClearOutChildren(BidirTop bidir, HasOneToManyJPA pojo,
 355                                   StartEnd startEnd, UnidirLevel unidirLevel,
 356                                   int expectedParent) throws Exception {
 357    UnidirTop unidir = newUnidir(unidirLevel);
 358    HasKeyPkJPA hasKeyPk = new HasKeyPkJPA();
 359
 360    pojo.getUnidirChildren().add(unidir);
 361    pojo.getHasKeyPks().add(hasKeyPk);
 362    pojo.getBidirChildren().add(bidir);
 363    bidir.setParent(pojo);
 364
 365    startEnd.start();
 366    em.persist(pojo);
 367    startEnd.end();
 368
 369    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), expectedParent, 1);
 370
 371    startEnd.start();
 372    pojo.getUnidirChildren().clear();
 373    pojo.getHasKeyPks().clear();
 374    pojo.getBidirChildren().clear();
 375    em.merge(pojo);
 376    startEnd.end();
 377
 378    try {
 379      ds.get(KeyFactory.stringToKey(unidir.getId()));
 380      fail("expected enfe");
 381    } catch (EntityNotFoundException enfe) {
 382      // good
 383    }
 384
 385    try {
 386      ds.get(hasKeyPk.getId());
 387      fail("expected enfe");
 388    } catch (EntityNotFoundException enfe) {
 389      // good
 390    }
 391
 392    try {
 393      ds.get(KeyFactory.stringToKey(bidir.getId()));
 394      fail("expected enfe");
 395    } catch (EntityNotFoundException enfe) {
 396      // good
 397    }
 398
 399    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 400    assertEquals(4, pojoEntity.getProperties().size());
 401    assertTrue(pojoEntity.hasProperty("bidirChildren"));
 402    assertNull(pojoEntity.getProperty("bidirChildren"));
 403    assertTrue(pojoEntity.hasProperty("unidirChildren"));
 404    assertNull(pojoEntity.getProperty("unidirChildren"));
 405    assertTrue(pojoEntity.hasProperty("hasKeyPks"));
 406    assertNull(pojoEntity.getProperty("hasKeyPks"));
 407
 408    assertCountsInDatastore(pojo.getClass(), bidir.getClass(), expectedParent, 0);
 409  }
 410
 411  void testFindWithOrderBy(Class<? extends HasPolymorphicRelationsListJPA.HasOneToManyWithOrderByJPA> pojoClass,
 412                           StartEnd startEnd) throws Exception {
 413    getExecutionContext().getNucleusContext().getPersistenceConfiguration().setProperty(
 414        "datanucleus.appengine.allowMultipleRelationsOfSameType", true);
 415
 416    Entity pojoEntity = new Entity(getEntityKind(pojoClass));
 417    ds.put(pojoEntity);
 418
 419    Entity unidirEntity1 = newUnidirEntity(UnidirLevel.Bottom, pojoEntity.getKey(), "name1", "str 1");
 420    ds.put(unidirEntity1);
 421
 422    Entity unidirEntity2 = newUnidirEntity(UnidirLevel.Middle, pojoEntity.getKey(), "name2", "str 2");
 423    ds.put(unidirEntity2);
 424
 425    Entity unidirEntity3 = newUnidirEntity(UnidirLevel.Top, pojoEntity.getKey(), "name1", "str 0");
 426    ds.put(unidirEntity3);
 427
 428    pojoEntity.setProperty("unidirByNameAndStr", Utils.newArrayList(unidirEntity2.getKey(), unidirEntity3.getKey(), unidirEntity1.getKey()));
 429    pojoEntity.setProperty("unidirByIdAndName", Utils.newArrayList(unidirEntity3.getKey(), unidirEntity2.getKey(), unidirEntity1.getKey()));
 430    pojoEntity.setProperty("unidirByNameAndId", Utils.newArrayList(unidirEntity2.getKey(), unidirEntity1.getKey(), unidirEntity3.getKey()));
 431    ds.put(pojoEntity);
 432
 433    startEnd.start();
 434    registerSubclasses();
 435    HasPolymorphicRelationsListJPA.HasOneToManyWithOrderByJPA pojo =
 436        em.find(pojoClass, KeyFactory.keyToString(pojoEntity.getKey()));
 437    assertNotNull(pojo);
 438    assertNotNull(pojo.getUnidirByNameAndStr());
 439    assertEquals(3, pojo.getUnidirByNameAndStr().size());
 440    assertEquals("str 2", pojo.getUnidirByNameAndStr().get(0).getStr());
 441    assertTrue(pojo.getUnidirByNameAndStr().get(0) instanceof UnidirMiddle);
 442    assertEquals("str 0", pojo.getUnidirByNameAndStr().get(1).getStr());
 443    assertTrue(pojo.getUnidirByNameAndStr().get(1) instanceof UnidirTop);
 444    assertEquals("str 1", pojo.getUnidirByNameAndStr().get(2).getStr());
 445    assertTrue(pojo.getUnidirByNameAndStr().get(2) instanceof UnidirBottom);
 446
 447    assertNotNull(pojo.getUnidirByIdAndName());
 448    assertEquals(3, pojo.getUnidirByIdAndName().size());
 449    assertEquals("str 0", pojo.getUnidirByIdAndName().get(0).getStr());
 450    assertTrue(pojo.getUnidirByIdAndName().get(0) instanceof UnidirTop);
 451    assertEquals("str 2", pojo.getUnidirByIdAndName().get(1).getStr());
 452    assertTrue(pojo.getUnidirByIdAndName().get(1) instanceof UnidirMiddle);
 453    assertEquals("str 1", pojo.getUnidirByIdAndName().get(2).getStr());
 454    assertTrue(pojo.getUnidirByIdAndName().get(2) instanceof UnidirBottom);
 455
 456    assertNotNull(pojo.getUnidirByNameAndId());
 457    assertEquals(3, pojo.getUnidirByNameAndId().size());
 458    assertEquals("str 2", pojo.getUnidirByNameAndId().get(0).getStr());
 459    assertTrue(pojo.getUnidirByNameAndId().get(0) instanceof UnidirMiddle);
 460    assertEquals("str 1", pojo.getUnidirByNameAndId().get(1).getStr());
 461    assertTrue(pojo.getUnidirByNameAndId().get(1) instanceof UnidirBottom);
 462    assertEquals("str 0", pojo.getUnidirByNameAndId().get(2).getStr());
 463    assertTrue(pojo.getUnidirByNameAndId().get(2) instanceof UnidirTop);
 464
 465    startEnd.end();
 466  }
 467
 468  void testFind(Class<? extends HasOneToManyJPA> pojoClass,
 469                Class<? extends BidirTop> bidirClass,
 470                StartEnd startEnd, UnidirLevel unidirLevel, String bidirKind) throws Exception {
 471    Entity pojoEntity = new Entity(getEntityKind(pojoClass));
 472    ds.put(pojoEntity);
 473
 474    Entity unidirEntity = newUnidirEntity(unidirLevel, pojoEntity.getKey(), "name1", "str 1");
 475    ds.put(unidirEntity);
 476
 477    Entity hasKeyPkEntity = new Entity(getEntityKind(HasKeyPkJPA.class), pojoEntity.getKey());
 478    hasKeyPkEntity.setProperty("str", "yar");
 479    ds.put(hasKeyPkEntity);
 480
 481    Entity bidirEntity = new Entity(bidirKind, pojoEntity.getKey());
 482    bidirEntity.setProperty("childVal", "yap");
 483    bidirEntity.setProperty("DTYPE", bidirClass.getName());
 484    ds.put(bidirEntity);
 485
 486    pojoEntity.setProperty("unidirChildren", Utils.newArrayList(unidirEntity.getKey()));
 487    pojoEntity.setProperty("hasKeyPks", Utils.newArrayList(hasKeyPkEntity.getKey()));
 488    pojoEntity.setProperty("bidirChildren", Utils.newArrayList(bidirEntity.getKey()));
 489    ds.put(pojoEntity);
 490
 491    startEnd.start();
 492    registerSubclasses();
 493    HasOneToManyJPA pojo = em.find(pojoClass, KeyFactory.keyToString(pojoEntity.getKey()));
 494    assertNotNull(pojo);
 495    assertNotNull(pojo.getUnidirChildren());
 496    assertEquals(1, pojo.getUnidirChildren().size());
 497    assertEquals(unidirLevel.clazz, pojo.getUnidirChildren().iterator().next().getClass());
 498    assertEquals("name1", pojo.getUnidirChildren().iterator().next().getName());
 499    assertNotNull(pojo.getHasKeyPks());
 500    assertEquals(1, pojo.getHasKeyPks().size());
 501    assertEquals("yar", pojo.getHasKeyPks().iterator().next().getStr());
 502    assertNotNull(pojo.getBidirChildren());
 503    assertEquals(bidirClass, pojo.getBidirChildren().iterator().next().getClass());
 504    assertEquals("yap", pojo.getBidirChildren().iterator().next().getChildVal());
 505    assertEquals(pojo, pojo.getBidirChildren().iterator().next().getParent());
 506    startEnd.end();
 507  }
 508
 509  void testQuery(Class<? extends HasOneToManyJPA> pojoClass,
 510                 Class<? extends BidirTop> bidirClass,
 511                 StartEnd startEnd, UnidirLevel unidirLevel, String bidirKind) throws Exception {
 512    Entity pojoEntity = new Entity(getEntityKind(pojoClass));
 513    ds.put(pojoEntity);
 514
 515    Entity unidirEntity = newUnidirEntity(unidirLevel, pojoEntity.getKey(), "name", "the str");
 516    ds.put(unidirEntity);
 517
 518    Entity hasKeyPkEntity = new Entity(getEntityKind(HasKeyPkJPA.class), pojoEntity.getKey());
 519    hasKeyPkEntity.setProperty("str", "yar");
 520    ds.put(hasKeyPkEntity);
 521
 522    Entity bidirEntity = new Entity(bidirKind, pojoEntity.getKey());
 523    bidirEntity.setProperty("childVal", "yap");
 524    bidirEntity.setProperty("DTYPE", bidirClass.getName());
 525    ds.put(bidirEntity);
 526
 527    pojoEntity.setProperty("unidirChildren", Utils.newArrayList(unidirEntity.getKey()));
 528    pojoEntity.setProperty("hasKeyPks", Utils.newArrayList(hasKeyPkEntity.getKey()));
 529    pojoEntity.setProperty("bidirChildren", Utils.newArrayList(bidirEntity.getKey()));
 530    ds.put(pojoEntity);
 531
 532    startEnd.start();
 533    registerSubclasses();
 534    javax.persistence.Query q = em.createQuery(
 535        "select from " + pojoClass.getName() + " b where id = :key");
 536    q.setParameter("key", KeyFactory.keyToString(pojoEntity.getKey()));
 537    @SuppressWarnings("unchecked")
 538    List<HasOneToManyJPA> result = (List<HasOneToManyJPA>) q.getResultList();
 539    assertEquals(1, result.size());
 540    HasOneToManyJPA pojo = result.get(0);
 541    assertNotNull(pojo.getUnidirChildren());
 542    assertEquals(1, pojo.getUnidirChildren().size());
 543    assertEquals(unidirLevel.clazz, pojo.getUnidirChildren().iterator().next().getClass());
 544    assertEquals("name", pojo.getUnidirChildren().iterator().next().getName());
 545    assertEquals(1, pojo.getUnidirChildren().size());
 546    assertNotNull(pojo.getHasKeyPks());
 547    assertEquals(1, pojo.getHasKeyPks().size());
 548    assertEquals("yar", pojo.getHasKeyPks().iterator().next().getStr());
 549    assertNotNull(pojo.getBidirChildren());
 550    assertEquals(1, pojo.getBidirChildren().size());
 551    assertEquals(bidirClass, pojo.getBidirChildren().iterator().next().getClass());
 552    assertEquals("yap", pojo.getBidirChildren().iterator().next().getChildVal());
 553    assertEquals(pojo, pojo.getBidirChildren().iterator().next().getParent());
 554    startEnd.end();
 555  }
 556
 557  void testChildFetchedLazily(Class<? extends HasOneToManyJPA> pojoClass,
 558                              Class<? extends BidirTop> bidirClass,
 559                              UnidirLevel unidirLevel, String bidirKind) throws Exception {
 560    DatastoreServiceConfig config = getStoreManager().getDefaultDatastoreServiceConfigForReads();
 561    // force a new emf to get created after we've installed our own DatastoreService mock
 562    if (emf.isOpen()) {
 563      emf.close();
 564    }
 565    tearDown();
 566
 567    DatastoreService mockDatastore = EasyMock.createMock(DatastoreService.class);
 568    DatastoreService original = DatastoreServiceFactoryInternal.getDatastoreService(config);
 569    DatastoreServiceFactoryInternal.setDatastoreService(mockDatastore);
 570    try {
 571      setUp();
 572
 573      Entity pojoEntity = new Entity(getEntityKind(pojoClass));
 574      ds.put(pojoEntity);
 575
 576      Entity unidirEntity = newUnidirEntity(unidirLevel, pojoEntity.getKey(), "name", "the str");
 577      ds.put(unidirEntity);
 578
 579      Entity hasKeyPkEntity = new Entity(getEntityKind(HasKeyPkJPA.class), pojoEntity.getKey());
 580      hasKeyPkEntity.setProperty("str", "yar");
 581      ds.put(hasKeyPkEntity);
 582
 583      Entity bidirEntity = new Entity(bidirKind, pojoEntity.getKey());
 584      bidirEntity.setProperty("childVal", "yap");
 585      ds.put(bidirEntity);
 586
 587      Transaction txn = EasyMock.createMock(Transaction.class);
 588      EasyMock.expect(txn.getId()).andReturn("1").times(2);
 589      txn.commit();
 590      EasyMock.expectLastCall();
 591      EasyMock.replay(txn);
 592      EasyMock.expect(mockDatastore.beginTransaction(EasyMock.isA(TransactionOptions.class))).andReturn(txn);
 593      // the only get we're going to perform is for the pojo
 594      EasyMock.expect(mockDatastore.get(txn, pojoEntity.getKey())).andReturn(pojoEntity);
 595      EasyMock.replay(mockDatastore);
 596
 597      beginTxn();
 598      HasOneToManyJPA pojo = em.find(pojoClass, KeyFactory.keyToString(pojoEntity.getKey()));
 599      assertNotNull(pojo);
 600      pojo.getId();
 601      commitTxn();
 602    } finally {
 603      // need to close the pmf before we restore the original datastore service
 604      emf.close();
 605      DatastoreServiceFactoryInternal.setDatastoreService(original);
 606    }
 607    EasyMock.verify(mockDatastore);
 608  }
 609
 610  void testDeleteParentDeletesChild(Class<? extends HasOneToManyJPA> pojoClass,
 611                                    Class<? extends BidirTop> bidirClass,
 612                                    StartEnd startEnd, UnidirLevel unidirLevel, String bidirKind) throws Exception {
 613    Entity pojoEntity = new Entity(getEntityKind(pojoClass));
 614    ds.put(pojoEntity);
 615
 616    Entity unidirEntity = newUnidirEntity(unidirLevel, pojoEntity.getKey(), "name", "the str");
 617    ds.put(unidirEntity);
 618
 619    Entity hasKeyPkEntity = new Entity(getEntityKind(HasKeyPkJPA.class), pojoEntity.getKey());
 620    hasKeyPkEntity.setProperty("str", "yar");
 621    ds.put(hasKeyPkEntity);
 622
 623    Entity bidirEntity = new Entity(bidirKind, pojoEntity.getKey());
 624    bidirEntity.setProperty("childVal", "yap");
 625    bidirEntity.setProperty("DTYPE", bidirClass.getName());
 626    ds.put(bidirEntity);
 627
 628    pojoEntity.setProperty("unidirChildren", Utils.newArrayList(unidirEntity.getKey()));
 629    pojoEntity.setProperty("hasKeyPks", Utils.newArrayList(hasKeyPkEntity.getKey()));
 630    pojoEntity.setProperty("bidirChildren", Utils.newArrayList(bidirEntity.getKey()));
 631    ds.put(pojoEntity);
 632
 633    startEnd.start();
 634    registerSubclasses();
 635    HasOneToManyJPA pojo = em.find(pojoClass, KeyFactory.keyToString(pojoEntity.getKey()));
 636    em.remove(pojo);
 637    startEnd.end();
 638    assertCountsInDatastore(pojoClass, bidirClass, 0, 0);
 639  }
 640
 641  private static int nextId = 0;
 642
 643  static String nextNamedKey() {
 644    return "a" + nextId++;
 645  }
 646
 647  public void testRemoveObject(HasOneToManyJPA pojo, BidirTop bidir1,
 648                               BidirTop bidir2,
 649                               StartEnd startEnd, UnidirLevel unidirLevel1,
 650                               UnidirLevel unidirLevel2, int count) throws Exception {
 651    pojo.setVal("yar");
 652    bidir1.setChildVal("yam1");
 653    bidir2.setChildVal("yam2");
 654    UnidirTop unidir1 = newUnidir(unidirLevel1);
 655    UnidirTop unidir2 = newUnidir(unidirLevel2);
 656    unidir2.setName("max");
 657    unidir2.setStr("another str");
 658    HasKeyPkJPA hasKeyPk1 = new HasKeyPkJPA(nextNamedKey());
 659    HasKeyPkJPA hasKeyPk2 = new HasKeyPkJPA(nextNamedKey());
 660    hasKeyPk2.setStr("yar 2");
 661    pojo.getUnidirChildren().add(unidir1);
 662    pojo.getUnidirChildren().add(unidir2);
 663    pojo.getHasKeyPks().add(hasKeyPk1);
 664    pojo.getHasKeyPks().add(hasKeyPk2);
 665    pojo.getBidirChildren().add(bidir1);
 666    pojo.getBidirChildren().add(bidir2);
 667
 668    startEnd.start();
 669    em.persist(pojo);
 670    startEnd.end();
 671
 672    assertCountsInDatastore(pojo.getClass(), bidir1.getClass(), count,  count + 1);
 673
 674    String bidir1Id = bidir1.getId();
 675    String unidir1Id = unidir1.getId();
 676    Key hasKeyPk1Key = hasKeyPk1.getId();
 677    pojo.getBidirChildren().remove(bidir1);
 678    pojo.getUnidirChildren().remove(unidir1);
 679    pojo.getHasKeyPks().remove(hasKeyPk1);
 680
 681    startEnd.start();
 682    em.merge(pojo);
 683    startEnd.end();
 684
 685    startEnd.start();
 686    pojo = em.find(pojo.getClass(), pojo.getId());
 687    assertNotNull(pojo.getId());
 688    assertEquals(1, pojo.getUnidirChildren().size());
 689    assertEquals(1, pojo.getHasKeyPks().size());
 690    assertEquals(1, pojo.getBidirChildren().size());
 691    assertNotNull(bidir2.getId());
 692    assertNotNull(bidir2.getParent());
 693    assertNotNull(unidir2.getId());
 694    assertNotNull(hasKeyPk2.getId());
 695    assertEquals(bidir2.getId(), pojo.getBidirChildren().iterator().next().getId());
 696    assertEquals(hasKeyPk2.getId(), pojo.getHasKeyPks().iterator().next().getId());
 697    assertEquals(unidir2.getId(), pojo.getUnidirChildren().iterator().next().getId());
 698
 699    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 700    assertNotNull(pojoEntity);
 701    assertEquals(4, pojoEntity.getProperties().size());
 702    assertEquals(Collections.singletonList(KeyFactory.stringToKey(bidir2.getId())), pojoEntity.getProperty("bidirChildren"));
 703    assertEquals(Collections.singletonList(KeyFactory.stringToKey(unidir2.getId())), pojoEntity.getProperty("unidirChildren"));
 704    assertEquals(Collections.singletonList(hasKeyPk2.getId()), pojoEntity.getProperty("hasKeyPks"));
 705
 706    startEnd.end();
 707
 708    try {
 709      ds.get(KeyFactory.stringToKey(bidir1Id));
 710      fail("expected EntityNotFoundException");
 711    } catch (EntityNotFoundException enfe) {
 712      // good
 713    }
 714    try {
 715      ds.get(KeyFactory.stringToKey(unidir1Id));
 716      fail("expected EntityNotFoundException");
 717    } catch (EntityNotFoundException enfe) {
 718      // good
 719    }
 720    try {
 721      ds.get(hasKeyPk1Key);
 722      fail("expected EntityNotFoundException");
 723    } catch (EntityNotFoundException enfe) {
 724      // good
 725    }
 726    Entity bidirChildEntity = ds.get(KeyFactory.stringToKey(bidir2.getId()));
 727    assertNotNull(bidirChildEntity);
 728    assertEquals(bidir2.getClass().getName(), bidirChildEntity.getProperty("DTYPE"));
 729    assertEquals(bidir2.getPropertyCount(), bidirChildEntity.getProperties().size());
 730    assertEquals("yam2", bidirChildEntity.getProperty("childVal"));
 731    assertEquals(KeyFactory.stringToKey(bidir2.getId()), bidirChildEntity.getKey());
 732    assertKeyParentEquals(pojo.getId(), bidirChildEntity, bidir2.getId());
 733
 734    Entity unidirEntity = ds.get(KeyFactory.stringToKey(unidir2.getId()));
 735    assertNotNull(unidirEntity);
 736    assertEquals("max", unidirEntity.getProperty("name"));
 737    assertEquals("another str", unidirEntity.getProperty("str"));
 738    assertEquals(KeyFactory.stringToKey(unidir2.getId()), unidirEntity.getKey());
 739    assertKeyParentEquals(pojo.getId(), unidirEntity, unidir2.getId());
 740
 741    Entity hasKeyPkEntity = ds.get(hasKeyPk2.getId());
 742    assertNotNull(hasKeyPkEntity);
 743    assertEquals("yar 2", hasKeyPkEntity.getProperty("str"));
 744    assertEquals(hasKeyPk2.getId(), hasKeyPkEntity.getKey());
 745    assertKeyParentEquals(pojo.getId(), hasKeyPkEntity, hasKeyPk2.getId());
 746
 747    Entity parentEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 748    assertNotNull(parentEntity);
 749    assertEquals(4, parentEntity.getProperties().size());
 750    assertEquals("yar", parentEntity.getProperty("val"));
 751    assertEquals(Collections.singletonList(bidirChildEntity.getKey()), parentEntity.getProperty("bidirChildren"));
 752    assertEquals(Collections.singletonList(unidirEntity.getKey()), parentEntity.getProperty("unidirChildren"));
 753    assertEquals(Collections.singletonList(hasKeyPkEntity.getKey()), parentEntity.getProperty("hasKeyPks"));
 754
 755    assertCountsInDatastore(pojo.getClass(), bidir1.getClass(), count, count);
 756  }
 757
 758  void testChangeParent(HasOneToManyJPA pojo, HasOneToManyJPA pojo2,
 759                        StartEnd startEnd) throws Exception {
 760    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_not_allowed);
 761    UnidirTop b1 = new UnidirTop();
 762    pojo.getUnidirChildren().add(b1);
 763
 764    startEnd.start();
 765    em.persist(pojo);
 766    startEnd.end();
 767
 768    startEnd.start();
 769    pojo2.getUnidirChildren().add(b1);
 770    try {
 771      em.persist(pojo2);
 772      startEnd.end();
 773      fail("expected exception");
 774    } catch (PersistenceException e) {
 775      rollbackTxn();
 776    }
 777  }
 778
 779  void testNewParentNewChild_NamedKeyOnChild(HasOneToManyJPA pojo,
 780                                             StartEnd startEnd) throws Exception {
 781    UnidirTop b1 = new UnidirTop("named key");
 782    pojo.getUnidirChildren().add(b1);
 783
 784    startEnd.start();
 785    em.persist(pojo);
 786    startEnd.end();
 787
 788    Entity unidirEntity = ds.get(KeyFactory.stringToKey(b1.getId()));
 789    assertEquals("named key", unidirEntity.getKey().getName());
 790  }
 791
 792  void testAddAlreadyPersistedChildToParent_NoTxnDifferentEm(HasOneToManyJPA pojo) {
 793    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
 794    UnidirTop unidir = new UnidirTop();
 795    em.persist(unidir);
 796    em.close();
 797    em = emf.createEntityManager();
 798    pojo.getUnidirChildren().add(unidir);
 799    try {
 800      em.persist(pojo);
 801      em.close();
 802      fail("expected exception");
 803    } catch (PersistenceException e) {
 804      // good
 805    }
 806
 807    assertEquals(0, countForClass(pojo.getClass()));
 808    assertEquals(1, countForClass(UnidirTop.class));
 809  }
 810
 811  void testAddAlreadyPersistedChildToParent_NoTxnSameEm(HasOneToManyJPA pojo) {
 812    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
 813    UnidirTop unidir = new UnidirTop();
 814    em.persist(unidir);
 815    em.close();
 816    em = emf.createEntityManager();
 817    pojo.getUnidirChildren().add(unidir);
 818    try {
 819      em.persist(pojo);
 820      em.close();
 821      fail("expected exception");
 822    } catch (PersistenceException e) {
 823      // good
 824    }
 825
 826    assertEquals(0, countForClass(pojo.getClass()));
 827    assertEquals(1, countForClass(UnidirTop.class));
 828  }
 829
 830  void testFetchOfOneToManyParentWithKeyPk(HasOneToManyKeyPkJPA pojo,
 831                                           StartEnd startEnd) throws Exception {
 832    startEnd.start();
 833    em.persist(pojo);
 834    startEnd.end();
 835
 836    startEnd.start();
 837    pojo = em.find(pojo.getClass(), pojo.getId());
 838    assertEquals(0, pojo.getUnidirChildren().size());
 839    startEnd.end();
 840  }
 841
 842  void testFetchOfOneToManyParentWithLongPk(HasOneToManyLongPkJPA pojo,
 843                                            StartEnd startEnd) throws Exception {
 844    startEnd.start();
 845    em.persist(pojo);
 846    startEnd.end();
 847
 848    startEnd.start();
 849    pojo = em.find(pojo.getClass(), pojo.getId());
 850    assertEquals(0, pojo.getUnidirChildren().size());
 851    startEnd.end();
 852  }
 853
 854  void testFetchOfOneToManyParentWithUnencodedStringPk(HasOneToManyUnencodedStringPkJPA pojo,
 855                                                       StartEnd startEnd) throws Exception {
 856    pojo.setId("yar");
 857    startEnd.start();
 858    em.persist(pojo);
 859    startEnd.end();
 860
 861    startEnd.start();
 862    pojo = em.find(pojo.getClass(), pojo.getId());
 863    assertEquals(0, pojo.getUnidirChildren().size());
 864    startEnd.end();
 865  }
 866
 867  void testAddChildToOneToManyParentWithLongPk(
 868      HasOneToManyLongPkJPA pojo, BidirTopLongPk bidirChild,
 869      StartEnd startEnd, UnidirLevel unidirLevel, String discriminator) throws Exception {
 870    startEnd.start();
 871    em.persist(pojo);
 872    startEnd.end();
 873
 874    startEnd.start();
 875    pojo = em.find(pojo.getClass(), pojo.getId());
 876    UnidirTop unidir = newUnidir(unidirLevel);
 877    pojo.getUnidirChildren().add(unidir);
 878    pojo.getBidirChildren().add(bidirChild);
 879    bidirChild.setParent(pojo);
 880    startEnd.end();
 881    Entity unidirEntity = ds.get(KeyFactory.stringToKey(unidir.getId()));
 882    assertEquals(unidirLevel.discriminator, unidirEntity.getProperty("DTYPE"));
 883    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidirChild.getId()));
 884    assertEquals(discriminator, bidirEntity.getProperty("DTYPE"));
 885    Entity pojoEntity = ds.get(KeyFactory.createKey(getEntityKind(pojo.getClass()), pojo.getId()));
 886    assertEquals(pojoEntity.getKey(), unidirEntity.getParent());
 887    assertEquals(pojoEntity.getKey(), bidirEntity.getParent());
 888  }
 889
 890  void testAddChildToOneToManyParentWithUnencodedStringPk(
 891      HasOneToManyUnencodedStringPkJPA pojo, BidirTopUnencodedStringPk bidirChild,
 892      StartEnd startEnd, UnidirLevel unidirLevel, String discriminator) throws Exception {
 893    pojo.setId("yar");
 894    startEnd.start();
 895    em.persist(pojo);
 896    startEnd.end();
 897
 898    startEnd.start();
 899    pojo = em.find(pojo.getClass(), pojo.getId());
 900    UnidirTop unidir = newUnidir(unidirLevel);
 901    pojo.getUnidirChildren().add(unidir);
 902    pojo.getBidirChildren().add(bidirChild);
 903    startEnd.end();
 904    
 905    Entity unidirEntity = ds.get(KeyFactory.stringToKey(unidir.getId()));
 906    assertNotNull(unidirEntity);
 907    
 908    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidirChild.getId()));
 909    assertNotNull(unidirEntity);
 910    assertEquals(bidirChild.getPropertyCount(), bidirEntity.getProperties().size());
 911    assertEquals(discriminator, bidirEntity.getProperty("TYPE"));
 912    
 913    Entity pojoEntity = ds.get(KeyFactory.createKey(getEntityKind(pojo.getClass()), pojo.getId()));
 914    assertEquals(pojoEntity.getKey(), unidirEntity.getParent());
 915    assertEquals(pojoEntity.getKey(), bidirEntity.getParent());
 916  }
 917
 918  void testAddQueriedParentToBidirChild(HasOneToManyJPA pojo, BidirTop bidir,
 919                                        StartEnd startEnd) throws Exception {
 920    startEnd.start();
 921    deleteAll(getEntityKind(pojo.getClass()));
 922    startEnd.end();
 923    
 924    startEnd.start();
 925    em.persist(pojo);
 926    startEnd.end();
 927
 928    startEnd.start();
 929    pojo = (HasOneToManyJPA) em.createQuery("select from " + pojo.getClass().getName() + " b").getSingleResult();
 930    bidir.setParent(pojo);
 931    pojo.getBidirChildren().add(bidir);
 932    em.persist(bidir);
 933    startEnd.end();
 934
 935    startEnd.start();
 936    pojo = (HasOneToManyJPA) em.createQuery("select from " + pojo.getClass().getName() + " b").getSingleResult();
 937    assertEquals(1, pojo.getBidirChildren().size());
 938    startEnd.end();
 939
 940    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidir.getId()));
 941    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 942    assertEquals(pojoEntity.getKey(), bidirEntity.getParent());
 943  }
 944
 945  void testAddFetchedParentToBidirChild(HasOneToManyJPA pojo, BidirTop bidir,
 946                                        StartEnd startEnd) throws Exception {
 947    startEnd.start();
 948    deleteAll(getEntityKind(pojo.getClass()));
 949    startEnd.end();
 950
 951    startEnd.start();
 952    em.persist(pojo);
 953    startEnd.end();
 954
 955    startEnd.start();
 956    pojo = em.find(pojo.getClass(), pojo.getId());
 957    bidir.setParent(pojo);
 958    pojo.getBidirChildren().add(bidir);
 959    em.persist(bidir);
 960    startEnd.end();
 961
 962    startEnd.start();
 963    pojo = (HasOneToManyJPA) em.createQuery("select from " + pojo.getClass().getName() + " b").getSingleResult();
 964    assertEquals(1, pojo.getBidirChildren().size());
 965    startEnd.end();
 966
 967    Entity bidirEntity = ds.get(KeyFactory.stringToKey(bidir.getId()));
 968    Entity pojoEntity = ds.get(KeyFactory.stringToKey(pojo.getId()));
 969    assertEquals(pojoEntity.getKey(), bidirEntity.getParent());
 970  }
 971
 972  private static final class PutPolicy implements DatastoreServiceInterceptor.Policy {
 973    private final List<Object[]> putParamList = Utils.newArrayList();
 974    public void intercept(Object o, Method method, Object[] params) {
 975      if (method.getName().equals("put")) {
 976        putParamList.add(params);
 977      }
 978    }
 979  }
 980
 981  PutPolicy setupPutPolicy(HasOneToManyJPA pojo, BidirTop bidir,
 982                           StartEnd startEnd) throws Throwable {
 983    PutPolicy policy = new PutPolicy();
 984    if (!em.isOpen()) {
 985      em = emf.createEntityManager();
 986    }
 987    DatastoreServiceInterceptor.install(getStoreManager(), policy);
 988    try {
 989      emf.close();
 990      switchDatasource(getEntityManagerFactoryName());
 991      UnidirTop unidir = new UnidirTop();
 992      pojo.getUnidirChildren().add(unidir);
 993      pojo.getBidirChildren().add(bidir);
 994      HasKeyPkJPA hasKeyPk = new HasKeyPkJPA();
 995      pojo.getHasKeyPks().add(hasKeyPk);
 996
 997      startEnd.start();
 998      em.persist(pojo);
 999      startEnd.end();
1000      // 1 put for the parent, 3 puts for the children, 1 more put
1001      // to add the child keys back on the parent
1002      assertEquals(5, policy.putParamList.size());
1003      policy.putParamList.clear();
1004      return policy;
1005    } catch (Throwable t) {
1006      DatastoreServiceInterceptor.uninstall();
1007      throw t;
1008    }
1009  }
1010
1011  void testOnlyOnePutOnChildUpdate(HasOneToManyJPA pojo, BidirTop bidir,
1012                                   StartEnd startEnd) throws Throwable {
1013    PutPolicy policy = setupPutPolicy(pojo, bidir, startEnd);
1014    try {
1015      startEnd.start();
1016      pojo = em.find(pojo.getClass(), pojo.getId());
1017      pojo.getUnidirChildren().iterator().next().setStr("some name");
1018      pojo.getBidirChildren().iterator().next().setChildVal("blarg");
1019      pojo.getHasKeyPks().iterator().next().setStr("double blarg");
1020      startEnd.end();
1021    } finally {
1022      DatastoreServiceInterceptor.uninstall();
1023    }
1024    // 1 put for each child update
1025    assertEquals(3, policy.putParamList.size());
1026  }
1027
1028  void testOnlyOneParentPutOnParentAndChildUpdate(HasOneToManyJPA pojo, BidirTop bidir,
1029                                                  StartEnd startEnd) throws Throwable {
1030    PutPolicy policy = setupPutPolicy(pojo, bidir, startEnd);
1031    try {
1032      startEnd.start();
1033      pojo = em.find(pojo.getClass(), pojo.getId());
1034      pojo.setVal("another val");
1035      pojo.getUnidirChildren().iterator().next().setStr("some name");
1036      pojo.getBidirChildren().iterator().next().setChildVal("blarg");
1037      pojo.getHasKeyPks().iterator().next().setStr("double blarg");
1038      startEnd.end();
1039    } finally {
1040      DatastoreServiceInterceptor.uninstall();
1041    }
1042    // 1 put for the parent update, 1 put for each child update
1043    assertEquals(4, policy.putParamList.size());
1044  }
1045
1046  void testOnlyOneParentPutOnChildDelete(HasOneToManyJPA pojo, BidirTop bidir,
1047                                         StartEnd startEnd,
1048                                         int expectedUpdatePuts) throws Throwable {
1049    PutPolicy policy = setupPutPolicy(pojo, bidir, startEnd);
1050    try {
1051      startEnd.start();
1052      pojo = em.find(pojo.getClass(), pojo.getId());
1053      pojo.setVal("another val");
1054      pojo.getUnidirChildren().clear();
1055      pojo.getBidirChildren().clear();
1056      pojo.getHasKeyPks().clear();
1057      startEnd.end();
1058    } finally {
1059      DatastoreServiceInterceptor.uninstall();
1060    }
1061    assertEquals(expectedUpdatePuts, policy.putParamList.size());
1062  }
1063
1064  void assertCountsInDatastore(Class<? extends HasOneToManyJPA> pojoClass,
1065                               Class<? extends BidirTop> bidirClass,
1066                               int expectedParent, int expectedChildren) {
1067    assertEquals(pojoClass.getName(), expectedParent, countForClass(pojoClass));
1068    assertEquals(bidirClass.getName(), expectedChildren, countForClass(bidirClass));
1069    assertEquals(UnidirTop.class.getName(), expectedChildren, countForClass(UnidirTop.class));
1070    assertEquals(HasKeyPkJPA.class.getName(), expectedChildren, countForClass(HasKeyPkJPA.class));
1071  }
1072
1073  UnidirTop newUnidir(UnidirLevel unidirLevel) {
1074    UnidirTop unidir = null;
1075    switch (unidirLevel) {
1076    case Top:
1077      unidir = new UnidirTop(nextNamedKey());
1078      unidir.setName("top name");
1079      unidir.setStr("top str");
1080      break;
1081    case Middle:
1082      unidir = new UnidirMiddle(nextNamedKey());
1083      unidir.setName("middle name");
1084      unidir.setStr("middle str");
1085      break;
1086    case Bottom:
1087      unidir = new UnidirBottom(nextNamedKey());
1088      unidir.setName("bottom name");
1089      unidir.setStr("bottom str");
1090      break;
1091    }
1092    return unidir;
1093  }
1094
1095  protected Entity newUnidirEntity(UnidirLevel unidirLevel, Key parentKey, String name, String str) {
1096    Entity e;
1097    String kind = getEntityKind(UnidirTop.class);
1098    if (parentKey != null) {
1099      e = new Entity(kind, parentKey);
1100    } else {
1101      e = new Entity(kind);
1102    }
1103    e.setProperty("name", name);
1104    e.setProperty("str", str);
1105    switch (unidirLevel) {
1106      case Top:
1107        e.setProperty("DTYPE", unidirLevel.discriminator);
1108        break;
1109      case Middle:
1110        e.setProperty("DTYPE", unidirLevel.discriminator);
1111        break;
1112      case Bottom:
1113        e.setProperty("DTYPE", unidirLevel.discriminator);
1114        break;
1115    }
1116    return e;
1117  }
1118  
1119  private void deleteAll(String kind) {
1120    Query q = new Query(kind);
1121     q.setKeysOnly();
1122     ds.delete(Utils.transform(ds.prepare(q).asIterable(), new Function<Entity, Key>() {
1123       public Key apply(Entity from) {
1124 	return from.getKey();
1125       }}));
1126  }
1127
1128  
1129  protected abstract void registerSubclasses();
1130}