PageRenderTime 116ms CodeModel.GetById 9ms app.highlight 98ms RepoModel.GetById 1ms app.codeStats 0ms

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

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