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