PageRenderTime 77ms CodeModel.GetById 15ms app.highlight 58ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://datanucleus-appengine.googlecode.com/
Java | 462 lines | 375 code | 52 blank | 35 comment | 21 complexity | a6f7d539c45159f02612e76b0186a808 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.DatastoreServiceFactory;
 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.DatastoreServiceRecordingImpl;
 28import com.google.appengine.datanucleus.DatastoreTestCase;
 29import com.google.appengine.datanucleus.TxnIdAnswer;
 30// TODO Should not be using these JDO model classes in a JPA test!!
 31import com.google.appengine.datanucleus.test.jdo.Flight;
 32import com.google.appengine.datanucleus.test.jdo.HasKeyAncestorKeyPkJDO;
 33import com.google.appengine.datanucleus.test.jpa.Book;
 34
 35import org.easymock.EasyMock;
 36
 37import javax.persistence.EntityManager;
 38import javax.persistence.EntityManagerFactory;
 39import javax.persistence.EntityTransaction;
 40import javax.persistence.Persistence;
 41import javax.persistence.Query;
 42
 43import static com.google.appengine.datanucleus.jpa.JPATestCase.EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed;
 44import static com.google.appengine.datanucleus.jpa.JPATestCase.EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_not_allowed;
 45import static com.google.appengine.datanucleus.jpa.JPATestCase.EntityManagerFactoryName.transactional_ds_non_transactional_ops_allowed;
 46import static com.google.appengine.datanucleus.jpa.JPATestCase.EntityManagerFactoryName.transactional_ds_non_transactional_ops_not_allowed;
 47
 48/**
 49 * Verifies jpa txn behavior across the following variables:
 50 * datasource type (txn | nontxn)
 51 * programmatic txn demarcation (yes | no)
 52 * operation (read | write)
 53 * support for that operation outside a txn (yes | no)
 54 *
 55 * See https://spreadsheets.google.com/a/google.com/pub?key=p8C3zgqqUfpstFKZ4ns1bQg
 56 * for all the gory details.
 57 *
 58 * @author Erick Armbrust <earmbrust@google.com>
 59 * @author Max Ross <maxr@google.com>
 60 */
 61public class JPATransactionTest extends DatastoreTestCase {
 62
 63  private DatastoreService ds;
 64  private DatastoreService mockDatastoreService = EasyMock.createMock(DatastoreService.class);
 65  private Transaction mockTxn = EasyMock.createMock(Transaction.class);
 66  private DatastoreServiceRecordingImpl recordingImpl;
 67  private TxnIdAnswer txnIdAnswer;
 68
 69  @Override
 70  protected void setUp() throws Exception {
 71    super.setUp();
 72    ds = DatastoreServiceFactory.getDatastoreService();
 73    txnIdAnswer = new TxnIdAnswer();
 74    recordingImpl =
 75        new DatastoreServiceRecordingImpl(mockDatastoreService, ds, mockTxn, txnIdAnswer);
 76    DatastoreServiceFactoryInternal.setDatastoreService(recordingImpl);
 77  }
 78
 79  @Override
 80  protected void tearDown() throws Exception {
 81    EasyMock.reset(mockDatastoreService, mockTxn);
 82    DatastoreServiceFactoryInternal.setDatastoreService(null);
 83    recordingImpl = null;
 84    super.tearDown();
 85  }
 86
 87  /**
 88   * A new EntityManagerFactory should be fetched on a per-test basis.  The
 89   * DatastoreService within the DatastorePersistenceHandler is obtained via the
 90   * DatastoreServiceFactory, so this ensures that the "injected" factory impl
 91   * is returned.
 92   */
 93  private EntityManagerFactory getEntityManagerFactory(String unit) {
 94    return Persistence.createEntityManagerFactory(unit);
 95  }
 96
 97  private void testWritePermutationWithExpectedDatastoreTxn(
 98      EntityManagerFactory emf, boolean explicitDemarcation) {
 99    if (explicitDemarcation) {
100      EasyMock.expect(mockDatastoreService.beginTransaction(EasyMock.isA(TransactionOptions.class))).andReturn(mockTxn);
101      EasyMock.expect(mockDatastoreService.put(
102          EasyMock.isA(com.google.appengine.api.datastore.Transaction.class),
103          EasyMock.isA(Entity.class))).andReturn(null);
104    } else {
105      EasyMock.expect(mockDatastoreService.put(EasyMock.isA(Entity.class))).andReturn(null);
106    }
107    EasyMock.expect(mockTxn.getId()).andAnswer(txnIdAnswer).anyTimes();
108    EasyMock.expect(mockTxn.isActive()).andReturn(true).anyTimes();
109    EasyMock.expect(mockTxn.getApp()).andReturn("test").anyTimes();
110    if (explicitDemarcation) {
111      mockTxn.commit();
112    }
113    EasyMock.replay(mockDatastoreService, mockTxn);
114
115    Book b1 = new Book();
116    b1.setTitle("Foo Bar");
117    b1.setAuthor("Joe Blow");
118    b1.setIsbn("12345");
119
120    EntityManager em = emf.createEntityManager();
121    EntityTransaction txn = em.getTransaction();
122    if (explicitDemarcation) {
123      txn.begin();
124    }
125    try {
126      em.persist(b1);
127    } finally {
128      if (explicitDemarcation) {
129        txn.commit();
130      }
131      em.close();
132    }
133    EasyMock.verify(mockDatastoreService, mockTxn);
134    EasyMock.reset(mockDatastoreService, mockTxn);
135  }
136
137  private void testReadPermutationWithExpectedDatastoreTxn(
138      EntityManagerFactory emf, boolean explicitDemarcation) throws EntityNotFoundException {
139
140    EasyMock.expect(mockDatastoreService.beginTransaction(EasyMock.isA(TransactionOptions.class))).andReturn(mockTxn);
141    EasyMock.expect(mockDatastoreService.get(
142        EasyMock.isA(com.google.appengine.api.datastore.Transaction.class),
143        EasyMock.isA(Key.class))).andReturn(null);
144    EasyMock.expect(mockTxn.getId()).andAnswer(txnIdAnswer).anyTimes();
145    EasyMock.expect(mockTxn.isActive()).andReturn(true).anyTimes();
146    EasyMock.expect(mockTxn.getApp()).andReturn("test").anyTimes();
147    mockTxn.commit();
148    EasyMock.replay(mockDatastoreService, mockTxn);
149
150    Entity entity = Book.newBookEntity("jimmy", "123456", "great american novel");
151    ds.put(entity);
152    EntityManager em = emf.createEntityManager();
153    EntityTransaction txn = em.getTransaction();
154    if (explicitDemarcation) {
155      txn.begin();
156    }
157    try {
158      em.find(Book.class, KeyFactory.keyToString(entity.getKey()));
159    } finally {
160      if (explicitDemarcation) {
161        txn.commit();
162      }
163      em.close();
164    }
165
166    EasyMock.verify(mockDatastoreService, mockTxn);
167    EasyMock.reset(mockDatastoreService, mockTxn);
168  }
169
170  private void testWritePermutationWithoutExpectedDatastoreTxn(
171      EntityManagerFactory emf, boolean explicitDemarcation) {
172    EasyMock.expect(mockDatastoreService.put(EasyMock.isA(Entity.class))).andReturn(null);
173    EasyMock.replay(mockDatastoreService, mockTxn);
174
175    Book b1 = new Book();
176    b1.setTitle("Foo Bar");
177    b1.setAuthor("Joe Blow");
178    b1.setIsbn("12345");
179
180    EntityManager em = emf.createEntityManager();
181    EntityTransaction txn = em.getTransaction();
182    if (explicitDemarcation) {
183      txn.begin();
184    }
185    try {
186      em.persist(b1);
187    } finally {
188      if (explicitDemarcation) {
189        txn.commit();
190      }
191      em.close();
192    }
193
194    EasyMock.verify(mockDatastoreService, mockTxn);
195    EasyMock.reset(mockDatastoreService, mockTxn);
196  }
197
198  private void testReadPermutationWithoutExpectedDatastoreTxn(
199      EntityManagerFactory emf, boolean explicitDemarcation) throws EntityNotFoundException {
200    EasyMock.expect(mockDatastoreService.get(EasyMock.isA(Key.class))).andReturn(null);
201    EasyMock.replay(mockDatastoreService, mockTxn);
202
203    Entity entity = Book.newBookEntity("jimmy", "123456", "great american novel");
204    ds.put(entity);
205    EntityManager em = emf.createEntityManager();
206    EntityTransaction txn = em.getTransaction();
207    if (explicitDemarcation) {
208      txn.begin();
209    }
210    try {
211      em.find(Book.class, KeyFactory.keyToString(entity.getKey()));
212    } finally {
213      if (explicitDemarcation) {
214        txn.commit();
215      }
216      em.close();
217    }
218    EasyMock.verify(mockDatastoreService, mockTxn);
219    EasyMock.reset(mockDatastoreService, mockTxn);
220  }
221
222  private static final boolean EXPLICIT_DEMARCATION = true;
223  private static final boolean NO_EXPLICIT_DEMARCATION = false;
224
225  public void testWritesWithDatastoreTxn() throws Exception {
226    EntityManagerFactory emf = getEntityManagerFactory(
227        transactional_ds_non_transactional_ops_not_allowed.name());
228    testWritePermutationWithExpectedDatastoreTxn(emf, EXPLICIT_DEMARCATION);
229    testWritePermutationWithExpectedDatastoreTxn(emf, NO_EXPLICIT_DEMARCATION);
230    emf.close();
231
232    emf = getEntityManagerFactory(transactional_ds_non_transactional_ops_allowed.name());
233    testWritePermutationWithExpectedDatastoreTxn(emf, EXPLICIT_DEMARCATION);
234    testWritePermutationWithExpectedDatastoreTxn(emf, NO_EXPLICIT_DEMARCATION);
235    emf.close();
236  }
237
238  public void testReadsWithDatastoreTxn() throws Exception {
239    EntityManagerFactory emf = getEntityManagerFactory(
240        transactional_ds_non_transactional_ops_not_allowed.name());
241    testReadPermutationWithExpectedDatastoreTxn(emf, EXPLICIT_DEMARCATION);
242    emf.close();
243    emf = getEntityManagerFactory(transactional_ds_non_transactional_ops_allowed.name());
244    testReadPermutationWithExpectedDatastoreTxn(emf, EXPLICIT_DEMARCATION);
245    emf.close();
246  }
247
248  public void testWritesWithoutDatastoreTxn() throws Exception {
249    EntityManagerFactory emf = getEntityManagerFactory(
250        nontransactional_ds_non_transactional_ops_allowed.name());
251    testWritePermutationWithoutExpectedDatastoreTxn(emf, EXPLICIT_DEMARCATION);
252    testWritePermutationWithoutExpectedDatastoreTxn(emf, NO_EXPLICIT_DEMARCATION);
253    emf.close();
254
255    emf = getEntityManagerFactory(nontransactional_ds_non_transactional_ops_not_allowed.name());
256    testWritePermutationWithoutExpectedDatastoreTxn(emf, EXPLICIT_DEMARCATION);
257    testWritePermutationWithoutExpectedDatastoreTxn(emf, NO_EXPLICIT_DEMARCATION);
258
259    emf.close();
260  }
261
262  public void testReadsWithoutDatastoreTxn() throws Exception {
263    EntityManagerFactory emf = getEntityManagerFactory(
264        transactional_ds_non_transactional_ops_allowed.name());
265    testReadPermutationWithoutExpectedDatastoreTxn(emf, NO_EXPLICIT_DEMARCATION);
266    emf.close();
267
268    emf = getEntityManagerFactory(nontransactional_ds_non_transactional_ops_allowed.name());
269    testReadPermutationWithoutExpectedDatastoreTxn(emf, EXPLICIT_DEMARCATION);
270    testReadPermutationWithoutExpectedDatastoreTxn(emf, NO_EXPLICIT_DEMARCATION);
271    emf.close();
272
273    emf = getEntityManagerFactory(nontransactional_ds_non_transactional_ops_not_allowed.name());
274    testReadPermutationWithoutExpectedDatastoreTxn(emf, EXPLICIT_DEMARCATION);
275    testReadPermutationWithoutExpectedDatastoreTxn(emf, NO_EXPLICIT_DEMARCATION);
276
277    emf.close();
278
279    emf = getEntityManagerFactory(transactional_ds_non_transactional_ops_not_allowed.name());
280    testReadPermutationWithoutExpectedDatastoreTxn(emf, NO_EXPLICIT_DEMARCATION);
281    emf.close();
282  }
283
284  private interface QueryRunner {
285    void runQuery(EntityManager em);
286    boolean isAncestor();
287  }
288
289  private void testQueryPermutationWithoutExpectedDatastoreTxn(
290      EntityManager em, boolean explicitDemarcation,
291      QueryRunner queryRunner) throws EntityNotFoundException {
292    if (queryRunner.isAncestor()) {
293      EasyMock.expect(mockDatastoreService.getCurrentTransaction(null)).andReturn(null);
294    }
295    EasyMock.expect(mockDatastoreService.prepare(
296        (com.google.appengine.api.datastore.Transaction) EasyMock.isNull(),
297        EasyMock.isA(com.google.appengine.api.datastore.Query.class))).andReturn(null);
298    EasyMock.replay(mockDatastoreService, mockTxn);
299
300    javax.persistence.EntityTransaction txn = em.getTransaction();
301    if (explicitDemarcation) {
302      txn.begin();
303    }
304    try {
305      queryRunner.runQuery(em);
306    } finally {
307      if (explicitDemarcation) {
308        txn.commit();
309      }
310    }
311    EasyMock.verify(mockDatastoreService, mockTxn);
312    EasyMock.reset(mockDatastoreService, mockTxn);
313  }
314
315  private void testQueryPermutationWithExpectedDatastoreTxn(
316      EntityManager em, boolean explicitDemarcation,
317      QueryRunner queryRunner) throws EntityNotFoundException {
318
319    EasyMock.expect(mockDatastoreService.beginTransaction(EasyMock.isA(TransactionOptions.class))).andReturn(mockTxn);
320    if (queryRunner.isAncestor()) {
321      EasyMock.expect(mockDatastoreService.getCurrentTransaction(null)).andReturn(mockTxn);
322    }
323    if (queryRunner.isAncestor()) {
324      EasyMock.expect(mockDatastoreService.prepare(
325          EasyMock.isA(com.google.appengine.api.datastore.Transaction.class),
326          EasyMock.isA(com.google.appengine.api.datastore.Query.class))).andReturn(null);
327    } else {
328      EasyMock.expect(mockDatastoreService.prepare(
329          (com.google.appengine.api.datastore.Transaction) EasyMock.isNull(),
330          EasyMock.isA(com.google.appengine.api.datastore.Query.class))).andReturn(null);
331    }
332    EasyMock.expect(mockTxn.getId()).andAnswer(txnIdAnswer).anyTimes();
333    EasyMock.expect(mockTxn.isActive()).andReturn(true).anyTimes();
334    EasyMock.expect(mockTxn.getApp()).andReturn("test").anyTimes();
335    mockTxn.commit();
336    EasyMock.replay(mockDatastoreService, mockTxn);
337
338    javax.persistence.EntityTransaction txn = em.getTransaction();
339    if (explicitDemarcation) {
340      txn.begin();
341    }
342    try {
343      queryRunner.runQuery(em);
344    } finally {
345      if (explicitDemarcation) {
346        txn.commit();
347      }
348    }
349
350    EasyMock.verify(mockDatastoreService, mockTxn);
351    EasyMock.reset(mockDatastoreService, mockTxn);
352  }
353
354  private static final QueryRunner ANCESTOR = new QueryRunner() {
355    public void runQuery(EntityManager em) {
356      Query q = em.createQuery("SELECT FROM " + HasKeyAncestorKeyPkJDO.class.getName() + " b WHERE ancestorKey = :p");
357      q.setParameter("p", KeyFactory.createKey("yar", 23));
358      q.getResultList();
359    }
360
361    public boolean isAncestor() {
362      return true;
363    }
364  };
365
366  private static final QueryRunner NON_ANCESTOR = new QueryRunner() {
367    public void runQuery(EntityManager em) {
368      Query q = em.createQuery("SELECT FROM " + Flight.class.getName() + " b");
369      q.getResultList();
370    }
371
372    public boolean isAncestor() {
373      return false;
374    }
375  };
376
377  public void testQueriesWithDatastoreTxn() throws Exception {
378    EntityManagerFactory emf = getEntityManagerFactory(transactional_ds_non_transactional_ops_allowed.name());
379    EntityManager em = emf.createEntityManager();
380    testQueryPermutationWithExpectedDatastoreTxn(em, EXPLICIT_DEMARCATION, ANCESTOR);
381    testQueryPermutationWithExpectedDatastoreTxn(em, EXPLICIT_DEMARCATION, NON_ANCESTOR);
382    em.close();
383    emf.close();
384    emf = getEntityManagerFactory(transactional_ds_non_transactional_ops_not_allowed.name());
385    em = emf.createEntityManager();
386    testQueryPermutationWithExpectedDatastoreTxn(em, EXPLICIT_DEMARCATION, ANCESTOR);
387    testQueryPermutationWithExpectedDatastoreTxn(em, EXPLICIT_DEMARCATION, NON_ANCESTOR);
388    em.close();
389    emf.close();
390  }
391
392  public void testQueriesWithoutDatastoreTxn() throws Exception {
393    EntityManagerFactory emf = getEntityManagerFactory(transactional_ds_non_transactional_ops_allowed.name());
394    EntityManager em = emf.createEntityManager();
395    testQueryPermutationWithoutExpectedDatastoreTxn(em, NO_EXPLICIT_DEMARCATION, ANCESTOR);
396    em.close();
397    emf.close();
398
399    emf = getEntityManagerFactory(transactional_ds_non_transactional_ops_not_allowed.name());
400    em = emf.createEntityManager();
401    testQueryPermutationWithoutExpectedDatastoreTxn(em, NO_EXPLICIT_DEMARCATION, ANCESTOR);
402    em.close();
403    emf.close();
404
405    emf = getEntityManagerFactory(nontransactional_ds_non_transactional_ops_not_allowed.name());
406    em = emf.createEntityManager();
407    testQueryPermutationWithoutExpectedDatastoreTxn(em, EXPLICIT_DEMARCATION, ANCESTOR);
408    testQueryPermutationWithoutExpectedDatastoreTxn(em, NO_EXPLICIT_DEMARCATION, ANCESTOR);
409    testQueryPermutationWithoutExpectedDatastoreTxn(em, EXPLICIT_DEMARCATION, NON_ANCESTOR);
410    testQueryPermutationWithoutExpectedDatastoreTxn(em, NO_EXPLICIT_DEMARCATION, NON_ANCESTOR);
411    em.close();
412    emf.close();
413
414    emf = getEntityManagerFactory(nontransactional_ds_non_transactional_ops_allowed.name());
415    em = emf.createEntityManager();
416    testQueryPermutationWithoutExpectedDatastoreTxn(em, EXPLICIT_DEMARCATION, ANCESTOR);
417    testQueryPermutationWithoutExpectedDatastoreTxn(em, NO_EXPLICIT_DEMARCATION, ANCESTOR);
418    testQueryPermutationWithoutExpectedDatastoreTxn(em, EXPLICIT_DEMARCATION, NON_ANCESTOR);
419    testQueryPermutationWithoutExpectedDatastoreTxn(em, NO_EXPLICIT_DEMARCATION, NON_ANCESTOR);
420    em.close();
421    emf.close();
422  }
423
424  public void testEmptyTxnBlock_Txn() {
425    EntityManagerFactory emf = getEntityManagerFactory(transactional_ds_non_transactional_ops_allowed.name());
426    EntityManager em = emf.createEntityManager();
427    try {
428      EasyMock.expect(mockDatastoreService.beginTransaction(EasyMock.isA(TransactionOptions.class))).andReturn(mockTxn);
429      EasyMock.expect(mockTxn.getId()).andAnswer(txnIdAnswer);
430      EasyMock.expect(mockTxn.getId()).andAnswer(txnIdAnswer);
431      mockTxn.commit();
432      EasyMock.expectLastCall();
433      EasyMock.replay(mockDatastoreService, mockTxn);
434      em.getTransaction().begin();
435      em.getTransaction().commit();
436      EasyMock.verify(mockDatastoreService, mockTxn);
437    } finally {
438      if (em.getTransaction().isActive()) {
439        em.getTransaction().rollback();
440      }
441      em.close();
442      emf.close();
443    }
444  }
445
446  public void testEmptyTxnBlock_NoTxn() {
447    EntityManagerFactory emf = getEntityManagerFactory(nontransactional_ds_non_transactional_ops_allowed.name());
448    EntityManager em = emf.createEntityManager();
449    try {
450      EasyMock.replay(mockDatastoreService, mockTxn);
451      em.getTransaction().begin();
452      em.getTransaction().commit();
453      EasyMock.verify(mockDatastoreService, mockTxn);
454    } finally {
455      if (em.getTransaction().isActive()) {
456        em.getTransaction().rollback();
457      }
458      em.close();
459      emf.close();
460    }
461  }
462}