1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.jdbc.datasource;
18
19 import java.sql.Connection;
20 import java.sql.DatabaseMetaData;
21 import java.sql.PreparedStatement;
22 import java.sql.SQLException;
23 import java.sql.Savepoint;
24 import javax.sql.DataSource;
25
26 import org.junit.After;
27 import org.junit.Before;
28 import org.junit.Test;
29 import org.mockito.InOrder;
30
31 import org.springframework.dao.DataAccessResourceFailureException;
32 import org.springframework.jdbc.UncategorizedSQLException;
33 import org.springframework.jdbc.support.nativejdbc.SimpleNativeJdbcExtractor;
34 import org.springframework.tests.Assume;
35 import org.springframework.tests.TestGroup;
36 import org.springframework.transaction.CannotCreateTransactionException;
37 import org.springframework.transaction.IllegalTransactionStateException;
38 import org.springframework.transaction.PlatformTransactionManager;
39 import org.springframework.transaction.TransactionDefinition;
40 import org.springframework.transaction.TransactionStatus;
41 import org.springframework.transaction.TransactionSystemException;
42 import org.springframework.transaction.TransactionTimedOutException;
43 import org.springframework.transaction.UnexpectedRollbackException;
44 import org.springframework.transaction.support.DefaultTransactionDefinition;
45 import org.springframework.transaction.support.TransactionCallbackWithoutResult;
46 import org.springframework.transaction.support.TransactionSynchronization;
47 import org.springframework.transaction.support.TransactionSynchronizationManager;
48 import org.springframework.transaction.support.TransactionTemplate;
49
50 import static org.junit.Assert.*;
51 import static org.mockito.BDDMockito.*;
52
53
54
55
56
57 public class DataSourceTransactionManagerTests {
58
59 private Connection con;
60
61 private DataSource ds;
62
63 private DataSourceTransactionManager tm;
64
65
66 @Before
67 public void setUp() throws Exception {
68 con = mock(Connection.class);
69 ds = mock(DataSource.class);
70 tm = new DataSourceTransactionManager(ds);
71 given(ds.getConnection()).willReturn(con);
72 }
73
74 @After
75 public void verifyTransactionSynchronizationManagerState() {
76 assertTrue(TransactionSynchronizationManager.getResourceMap().isEmpty());
77 assertFalse(TransactionSynchronizationManager.isSynchronizationActive());
78 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
79 assertFalse(TransactionSynchronizationManager.isActualTransactionActive());
80 }
81
82
83 @Test
84 public void testTransactionCommitWithAutoCommitTrue() throws Exception {
85 doTestTransactionCommitRestoringAutoCommit(true, false, false);
86 }
87
88 @Test
89 public void testTransactionCommitWithAutoCommitFalse() throws Exception {
90 doTestTransactionCommitRestoringAutoCommit(false, false, false);
91 }
92
93 @Test
94 public void testTransactionCommitWithAutoCommitTrueAndLazyConnection() throws Exception {
95 doTestTransactionCommitRestoringAutoCommit(true, true, false);
96 }
97
98 @Test
99 public void testTransactionCommitWithAutoCommitFalseAndLazyConnection() throws Exception {
100 doTestTransactionCommitRestoringAutoCommit(false, true, false);
101 }
102
103 @Test
104 public void testTransactionCommitWithAutoCommitTrueAndLazyConnectionAndStatementCreated() throws Exception {
105 doTestTransactionCommitRestoringAutoCommit(true, true, true);
106 }
107
108 @Test
109 public void testTransactionCommitWithAutoCommitFalseAndLazyConnectionAndStatementCreated() throws Exception {
110 doTestTransactionCommitRestoringAutoCommit(false, true, true);
111 }
112
113 private void doTestTransactionCommitRestoringAutoCommit(
114 boolean autoCommit, boolean lazyConnection, final boolean createStatement)
115 throws Exception {
116 if (lazyConnection) {
117 given(con.getAutoCommit()).willReturn(autoCommit);
118 given(con.getTransactionIsolation()).willReturn(Connection.TRANSACTION_READ_COMMITTED);
119 }
120
121 if (!lazyConnection || createStatement) {
122 given(con.getAutoCommit()).willReturn(autoCommit);
123 }
124
125 final DataSource dsToUse = (lazyConnection ? new LazyConnectionDataSourceProxy(ds) : ds);
126 tm = new DataSourceTransactionManager(dsToUse);
127 TransactionTemplate tt = new TransactionTemplate(tm);
128 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(dsToUse));
129 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
130
131 tt.execute(new TransactionCallbackWithoutResult() {
132 @Override
133 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
134 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dsToUse));
135 assertTrue("Synchronization active", TransactionSynchronizationManager.isSynchronizationActive());
136 assertTrue("Is new transaction", status.isNewTransaction());
137 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
138 assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
139 Connection tCon = DataSourceUtils.getConnection(dsToUse);
140 try {
141 if (createStatement) {
142 tCon.createStatement();
143 assertEquals(con, new SimpleNativeJdbcExtractor().getNativeConnection(tCon));
144 }
145 }
146 catch (SQLException ex) {
147 throw new UncategorizedSQLException("", "", ex);
148 }
149 }
150 });
151
152 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(dsToUse));
153 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
154
155 if(autoCommit && (!lazyConnection || createStatement)) {
156 InOrder ordered = inOrder(con);
157 ordered.verify(con).setAutoCommit(false);
158 ordered.verify(con).commit();
159 ordered.verify(con).setAutoCommit(true);
160 }
161 if (createStatement) {
162 verify(con, times(2)).close();
163 }
164 else {
165 verify(con).close();
166 }
167 }
168
169 @Test
170 public void testTransactionRollbackWithAutoCommitTrue() throws Exception {
171 doTestTransactionRollbackRestoringAutoCommit(true, false, false);
172 }
173
174 @Test
175 public void testTransactionRollbackWithAutoCommitFalse() throws Exception {
176 doTestTransactionRollbackRestoringAutoCommit(false, false, false);
177 }
178
179 @Test
180 public void testTransactionRollbackWithAutoCommitTrueAndLazyConnection() throws Exception {
181 doTestTransactionRollbackRestoringAutoCommit(true, true, false);
182 }
183
184 @Test
185 public void testTransactionRollbackWithAutoCommitFalseAndLazyConnection() throws Exception {
186 doTestTransactionRollbackRestoringAutoCommit(false, true, false);
187 }
188
189 @Test
190 public void testTransactionRollbackWithAutoCommitTrueAndLazyConnectionAndCreateStatement() throws Exception {
191 doTestTransactionRollbackRestoringAutoCommit(true, true, true);
192 }
193
194 @Test
195 public void testTransactionRollbackWithAutoCommitFalseAndLazyConnectionAndCreateStatement() throws Exception {
196 doTestTransactionRollbackRestoringAutoCommit(false, true, true);
197 }
198
199 private void doTestTransactionRollbackRestoringAutoCommit(
200 boolean autoCommit, boolean lazyConnection, final boolean createStatement) throws Exception {
201 if (lazyConnection) {
202 given(con.getAutoCommit()).willReturn(autoCommit);
203 given(con.getTransactionIsolation()).willReturn(
204 Connection.TRANSACTION_READ_COMMITTED);
205 }
206
207 if (!lazyConnection || createStatement) {
208 given(con.getAutoCommit()).willReturn(autoCommit);
209 }
210
211 final DataSource dsToUse = (lazyConnection ? new LazyConnectionDataSourceProxy(ds) : ds);
212 tm = new DataSourceTransactionManager(dsToUse);
213 TransactionTemplate tt = new TransactionTemplate(tm);
214 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(dsToUse));
215 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
216
217 final RuntimeException ex = new RuntimeException("Application exception");
218 try {
219 tt.execute(new TransactionCallbackWithoutResult() {
220 @Override
221 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
222 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dsToUse));
223 assertTrue("Synchronization active", TransactionSynchronizationManager.isSynchronizationActive());
224 assertTrue("Is new transaction", status.isNewTransaction());
225 Connection con = DataSourceUtils.getConnection(dsToUse);
226 if (createStatement) {
227 try {
228 con.createStatement();
229 }
230 catch (SQLException ex) {
231 throw new UncategorizedSQLException("", "", ex);
232 }
233 }
234 throw ex;
235 }
236 });
237 fail("Should have thrown RuntimeException");
238 }
239 catch (RuntimeException ex2) {
240
241 assertTrue("Correct exception thrown", ex2.equals(ex));
242 }
243
244 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
245 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
246
247 if(autoCommit && (!lazyConnection || createStatement)) {
248 InOrder ordered = inOrder(con);
249 ordered.verify(con).setAutoCommit(false);
250 ordered.verify(con).rollback();
251 ordered.verify(con).setAutoCommit(true);
252 }
253 if (createStatement) {
254 verify(con, times(2)).close();
255 }
256 else {
257 verify(con).close();
258 }
259 }
260
261 @Test
262 public void testTransactionRollbackOnly() throws Exception {
263 tm.setTransactionSynchronization(DataSourceTransactionManager.SYNCHRONIZATION_NEVER);
264 TransactionTemplate tt = new TransactionTemplate(tm);
265 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
266 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
267
268 ConnectionHolder conHolder = new ConnectionHolder(con);
269 conHolder.setTransactionActive(true);
270 TransactionSynchronizationManager.bindResource(ds, conHolder);
271 final RuntimeException ex = new RuntimeException("Application exception");
272 try {
273 tt.execute(new TransactionCallbackWithoutResult() {
274 @Override
275 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
276 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
277 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
278 assertTrue("Is existing transaction", !status.isNewTransaction());
279 throw ex;
280 }
281 });
282 fail("Should have thrown RuntimeException");
283 }
284 catch (RuntimeException ex2) {
285
286 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
287 assertEquals("Correct exception thrown", ex, ex2);
288 }
289 finally {
290 TransactionSynchronizationManager.unbindResource(ds);
291 }
292
293 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
294 }
295
296 @Test
297 public void testParticipatingTransactionWithRollbackOnly() throws Exception {
298 doTestParticipatingTransactionWithRollbackOnly(false);
299 }
300
301 @Test
302 public void testParticipatingTransactionWithRollbackOnlyAndFailEarly() throws Exception {
303 doTestParticipatingTransactionWithRollbackOnly(true);
304 }
305
306 private void doTestParticipatingTransactionWithRollbackOnly(boolean failEarly) throws Exception {
307 given(con.isReadOnly()).willReturn(false);
308 if (failEarly) {
309 tm.setFailEarlyOnGlobalRollbackOnly(true);
310 }
311 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
312 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
313
314 TransactionStatus ts = tm.getTransaction(new DefaultTransactionDefinition());
315 TestTransactionSynchronization synch =
316 new TestTransactionSynchronization(ds, TransactionSynchronization.STATUS_ROLLED_BACK);
317 TransactionSynchronizationManager.registerSynchronization(synch);
318
319 boolean outerTransactionBoundaryReached = false;
320 try {
321 assertTrue("Is new transaction", ts.isNewTransaction());
322
323 final TransactionTemplate tt = new TransactionTemplate(tm);
324 tt.execute(new TransactionCallbackWithoutResult() {
325 @Override
326 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
327 assertTrue("Is existing transaction", !status.isNewTransaction());
328 assertFalse("Is not rollback-only", status.isRollbackOnly());
329 tt.execute(new TransactionCallbackWithoutResult() {
330 @Override
331 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
332 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
333 assertTrue("Synchronization active", TransactionSynchronizationManager.isSynchronizationActive());
334 assertTrue("Is existing transaction", !status.isNewTransaction());
335 status.setRollbackOnly();
336 }
337 });
338 assertTrue("Is existing transaction", !status.isNewTransaction());
339 assertTrue("Is rollback-only", status.isRollbackOnly());
340 }
341 });
342
343 outerTransactionBoundaryReached = true;
344 tm.commit(ts);
345
346 fail("Should have thrown UnexpectedRollbackException");
347 }
348 catch (UnexpectedRollbackException ex) {
349
350 if (!outerTransactionBoundaryReached) {
351 tm.rollback(ts);
352 }
353 if (failEarly) {
354 assertFalse(outerTransactionBoundaryReached);
355 }
356 else {
357 assertTrue(outerTransactionBoundaryReached);
358 }
359 }
360
361 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
362 assertFalse(synch.beforeCommitCalled);
363 assertTrue(synch.beforeCompletionCalled);
364 assertFalse(synch.afterCommitCalled);
365 assertTrue(synch.afterCompletionCalled);
366 verify(con).rollback();
367 verify(con).close();
368 }
369
370 @Test
371 public void testParticipatingTransactionWithIncompatibleIsolationLevel() throws Exception {
372 tm.setValidateExistingTransaction(true);
373
374 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
375 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
376
377 try {
378 final TransactionTemplate tt = new TransactionTemplate(tm);
379 final TransactionTemplate tt2 = new TransactionTemplate(tm);
380 tt2.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
381
382 tt.execute(new TransactionCallbackWithoutResult() {
383 @Override
384 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
385 assertFalse("Is not rollback-only", status.isRollbackOnly());
386 tt2.execute(new TransactionCallbackWithoutResult() {
387 @Override
388 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
389 status.setRollbackOnly();
390 }
391 });
392 assertTrue("Is rollback-only", status.isRollbackOnly());
393 }
394 });
395
396 fail("Should have thrown IllegalTransactionStateException");
397 }
398 catch (IllegalTransactionStateException ex) {
399
400 }
401
402 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
403 verify(con).rollback();
404 verify(con).close();
405 }
406
407 @Test
408 public void testParticipatingTransactionWithIncompatibleReadOnly() throws Exception {
409 willThrow(new SQLException("read-only not supported")).given(con).setReadOnly(true);
410 tm.setValidateExistingTransaction(true);
411
412 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
413 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
414
415 try {
416 final TransactionTemplate tt = new TransactionTemplate(tm);
417 tt.setReadOnly(true);
418 final TransactionTemplate tt2 = new TransactionTemplate(tm);
419 tt2.setReadOnly(false);
420
421 tt.execute(new TransactionCallbackWithoutResult() {
422 @Override
423 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
424 assertFalse("Is not rollback-only", status.isRollbackOnly());
425 tt2.execute(new TransactionCallbackWithoutResult() {
426 @Override
427 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
428 status.setRollbackOnly();
429 }
430 });
431 assertTrue("Is rollback-only", status.isRollbackOnly());
432 }
433 });
434
435 fail("Should have thrown IllegalTransactionStateException");
436 }
437 catch (IllegalTransactionStateException ex) {
438
439 }
440
441 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
442 verify(con).rollback();
443 verify(con).close();
444 }
445
446 @Test
447 public void testParticipatingTransactionWithTransactionStartedFromSynch() throws Exception {
448 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
449 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
450
451 final TransactionTemplate tt = new TransactionTemplate(tm);
452 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
453
454 final TestTransactionSynchronization synch =
455 new TestTransactionSynchronization(ds, TransactionSynchronization.STATUS_COMMITTED) {
456 @Override
457 public void afterCompletion(int status) {
458 super.afterCompletion(status);
459 tt.execute(new TransactionCallbackWithoutResult() {
460 @Override
461 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
462 }
463 });
464 }
465 };
466
467 tt.execute(new TransactionCallbackWithoutResult() {
468 @Override
469 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
470 TransactionSynchronizationManager.registerSynchronization(synch);
471 }
472 });
473
474 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
475 assertTrue(synch.beforeCommitCalled);
476 assertTrue(synch.beforeCompletionCalled);
477 assertTrue(synch.afterCommitCalled);
478 assertTrue(synch.afterCompletionCalled);
479 verify(con, times(2)).commit();
480 verify(con, times(2)).close();
481 }
482
483 @Test
484 public void testParticipatingTransactionWithRollbackOnlyAndInnerSynch() throws Exception {
485 tm.setTransactionSynchronization(DataSourceTransactionManager.SYNCHRONIZATION_NEVER);
486 DataSourceTransactionManager tm2 = new DataSourceTransactionManager(ds);
487
488
489 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
490 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
491
492 TransactionStatus ts = tm.getTransaction(new DefaultTransactionDefinition());
493 final TestTransactionSynchronization synch =
494 new TestTransactionSynchronization(ds, TransactionSynchronization.STATUS_UNKNOWN);
495
496 try {
497 assertTrue("Is new transaction", ts.isNewTransaction());
498
499 final TransactionTemplate tt = new TransactionTemplate(tm2);
500 tt.execute(new TransactionCallbackWithoutResult() {
501 @Override
502 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
503 assertTrue("Is existing transaction", !status.isNewTransaction());
504 assertFalse("Is not rollback-only", status.isRollbackOnly());
505 tt.execute(new TransactionCallbackWithoutResult() {
506 @Override
507 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
508 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
509 assertTrue("Synchronization active", TransactionSynchronizationManager.isSynchronizationActive());
510 assertTrue("Is existing transaction", !status.isNewTransaction());
511 status.setRollbackOnly();
512 }
513 });
514 assertTrue("Is existing transaction", !status.isNewTransaction());
515 assertTrue("Is rollback-only", status.isRollbackOnly());
516 TransactionSynchronizationManager.registerSynchronization(synch);
517 }
518 });
519
520 tm.commit(ts);
521
522 fail("Should have thrown UnexpectedRollbackException");
523 }
524 catch (UnexpectedRollbackException ex) {
525
526 }
527
528 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
529 assertFalse(synch.beforeCommitCalled);
530 assertTrue(synch.beforeCompletionCalled);
531 assertFalse(synch.afterCommitCalled);
532 assertTrue(synch.afterCompletionCalled);
533 verify(con).rollback();
534 verify(con).close();
535 }
536
537 @Test
538 public void testPropagationRequiresNewWithExistingTransaction() throws Exception {
539 final TransactionTemplate tt = new TransactionTemplate(tm);
540 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
541 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
542 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
543
544 tt.execute(new TransactionCallbackWithoutResult() {
545 @Override
546 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
547 assertTrue("Is new transaction", status.isNewTransaction());
548 assertTrue("Synchronization active", TransactionSynchronizationManager.isSynchronizationActive());
549 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
550 assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
551 tt.execute(new TransactionCallbackWithoutResult() {
552 @Override
553 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
554 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
555 assertTrue("Synchronization active", TransactionSynchronizationManager.isSynchronizationActive());
556 assertTrue("Is new transaction", status.isNewTransaction());
557 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
558 assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
559 status.setRollbackOnly();
560 }
561 });
562 assertTrue("Is new transaction", status.isNewTransaction());
563 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
564 assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
565 }
566 });
567
568 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
569 verify(con).rollback();
570 verify(con).commit();
571 verify(con, times(2)).close();
572 }
573
574 @Test
575 public void testPropagationRequiresNewWithExistingTransactionAndUnrelatedDataSource() throws Exception {
576 Connection con2 = mock(Connection.class);
577 final DataSource ds2 = mock(DataSource.class);
578 given(ds2.getConnection()).willReturn(con2);
579
580 final TransactionTemplate tt = new TransactionTemplate(tm);
581 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
582
583 PlatformTransactionManager tm2 = new DataSourceTransactionManager(ds2);
584 final TransactionTemplate tt2 = new TransactionTemplate(tm2);
585 tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
586
587 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
588 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds2));
589 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
590
591 tt.execute(new TransactionCallbackWithoutResult() {
592 @Override
593 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
594 assertTrue("Is new transaction", status.isNewTransaction());
595 assertTrue("Synchronization active", TransactionSynchronizationManager.isSynchronizationActive());
596 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
597 assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
598 tt2.execute(new TransactionCallbackWithoutResult() {
599 @Override
600 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
601 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
602 assertTrue("Synchronization active", TransactionSynchronizationManager.isSynchronizationActive());
603 assertTrue("Is new transaction", status.isNewTransaction());
604 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
605 assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
606 status.setRollbackOnly();
607 }
608 });
609 assertTrue("Is new transaction", status.isNewTransaction());
610 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
611 assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
612 }
613 });
614
615 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
616 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds2));
617 verify(con).commit();
618 verify(con).close();
619 verify(con2).rollback();
620 verify(con2).close();
621 }
622
623 @Test
624 public void testPropagationRequiresNewWithExistingTransactionAndUnrelatedFailingDataSource() throws Exception {
625 final DataSource ds2 = mock(DataSource.class);
626 SQLException failure = new SQLException();
627 given(ds2.getConnection()).willThrow(failure);
628
629
630 final TransactionTemplate tt = new TransactionTemplate(tm);
631 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
632
633 DataSourceTransactionManager tm2 = new DataSourceTransactionManager(ds2);
634 tm2.setTransactionSynchronization(DataSourceTransactionManager.SYNCHRONIZATION_NEVER);
635 final TransactionTemplate tt2 = new TransactionTemplate(tm2);
636 tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
637
638 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
639 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds2));
640 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
641
642 try {
643 tt.execute(new TransactionCallbackWithoutResult() {
644 @Override
645 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
646 assertTrue("Is new transaction", status.isNewTransaction());
647 assertTrue("Synchronization active", TransactionSynchronizationManager.isSynchronizationActive());
648 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
649 assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
650 tt2.execute(new TransactionCallbackWithoutResult() {
651 @Override
652 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
653 status.setRollbackOnly();
654 }
655 });
656 }
657 });
658 fail("Should have thrown CannotCreateTransactionException");
659 }
660 catch (CannotCreateTransactionException ex) {
661 assertSame(failure, ex.getCause());
662 }
663
664 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
665 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds2));
666 verify(con).rollback();
667 verify(con).close();
668 }
669
670 @Test
671 public void testPropagationNotSupportedWithExistingTransaction() throws Exception {
672 final TransactionTemplate tt = new TransactionTemplate(tm);
673 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
674 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
675 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
676
677 tt.execute(new TransactionCallbackWithoutResult() {
678 @Override
679 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
680 assertTrue("Is new transaction", status.isNewTransaction());
681 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
682 assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
683 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
684 tt.execute(new TransactionCallbackWithoutResult() {
685 @Override
686 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
687 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
688 assertTrue("Synchronization active", TransactionSynchronizationManager.isSynchronizationActive());
689 assertTrue("Isn't new transaction", !status.isNewTransaction());
690 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
691 assertFalse(TransactionSynchronizationManager.isActualTransactionActive());
692 status.setRollbackOnly();
693 }
694 });
695 assertTrue("Is new transaction", status.isNewTransaction());
696 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
697 assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
698 }
699 });
700
701 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
702 verify(con).commit();
703 verify(con).close();
704 }
705
706 @Test
707 public void testPropagationNeverWithExistingTransaction() throws Exception {
708 final TransactionTemplate tt = new TransactionTemplate(tm);
709 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
710 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
711 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
712
713 try {
714 tt.execute(new TransactionCallbackWithoutResult() {
715 @Override
716 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
717 assertTrue("Is new transaction", status.isNewTransaction());
718 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NEVER);
719 tt.execute(new TransactionCallbackWithoutResult() {
720 @Override
721 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
722 fail("Should have thrown IllegalTransactionStateException");
723 }
724 });
725 fail("Should have thrown IllegalTransactionStateException");
726 }
727 });
728 }
729 catch (IllegalTransactionStateException ex) {
730
731 }
732
733 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
734 verify(con).rollback();
735 verify(con).close();
736 }
737
738 @Test
739 public void testPropagationSupportsAndRequiresNew() throws Exception {
740 TransactionTemplate tt = new TransactionTemplate(tm);
741 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
742 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
743 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
744
745 tt.execute(new TransactionCallbackWithoutResult() {
746 @Override
747 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
748 assertTrue("Synchronization active", TransactionSynchronizationManager.isSynchronizationActive());
749 TransactionTemplate tt2 = new TransactionTemplate(tm);
750 tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
751 tt2.execute(new TransactionCallbackWithoutResult() {
752 @Override
753 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
754 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
755 assertTrue("Synchronization active", TransactionSynchronizationManager.isSynchronizationActive());
756 assertTrue("Is new transaction", status.isNewTransaction());
757 assertSame(con, DataSourceUtils.getConnection(ds));
758 assertSame(con, DataSourceUtils.getConnection(ds));
759 }
760 });
761 }
762 });
763
764 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
765 verify(con).commit();
766 verify(con).close();
767 }
768
769 @Test
770 public void testPropagationSupportsAndRequiresNewWithEarlyAccess() throws Exception {
771 final Connection con1 = mock(Connection.class);
772 final Connection con2 = mock(Connection.class);
773 given(ds.getConnection()).willReturn(con1, con2);
774
775 final
776 TransactionTemplate tt = new TransactionTemplate(tm);
777 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
778 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
779 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
780
781 tt.execute(new TransactionCallbackWithoutResult() {
782 @Override
783 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
784 assertTrue("Synchronization active", TransactionSynchronizationManager.isSynchronizationActive());
785 assertSame(con1, DataSourceUtils.getConnection(ds));
786 assertSame(con1, DataSourceUtils.getConnection(ds));
787 TransactionTemplate tt2 = new TransactionTemplate(tm);
788 tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
789 tt2.execute(new TransactionCallbackWithoutResult() {
790 @Override
791 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
792 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
793 assertTrue("Synchronization active", TransactionSynchronizationManager.isSynchronizationActive());
794 assertTrue("Is new transaction", status.isNewTransaction());
795 assertSame(con2, DataSourceUtils.getConnection(ds));
796 assertSame(con2, DataSourceUtils.getConnection(ds));
797 }
798 });
799 assertSame(con1, DataSourceUtils.getConnection(ds));
800 }
801 });
802
803 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
804 verify(con1).close();
805 verify(con2).commit();
806 verify(con2).close();
807 }
808
809 @Test
810 public void testTransactionWithIsolationAndReadOnly() throws Exception {
811 given(con.getTransactionIsolation()).willReturn(Connection.TRANSACTION_READ_COMMITTED);
812 given(con.getAutoCommit()).willReturn(true);
813
814 TransactionTemplate tt = new TransactionTemplate(tm);
815 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
816 tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
817 tt.setReadOnly(true);
818 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
819 tt.execute(new TransactionCallbackWithoutResult() {
820 @Override
821 protected void doInTransactionWithoutResult(TransactionStatus status) {
822 assertTrue(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
823 assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
824
825 }
826 });
827
828 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
829 InOrder ordered = inOrder(con);
830 ordered.verify(con).setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
831 ordered.verify(con).setAutoCommit(false);
832 ordered.verify(con).commit();
833 ordered.verify(con).setAutoCommit(true);
834 ordered.verify(con).setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
835 verify(con).close();
836 }
837
838 @Test
839 public void testTransactionWithLongTimeout() throws Exception {
840 doTestTransactionWithTimeout(10);
841 }
842
843 @Test
844 public void testTransactionWithShortTimeout() throws Exception {
845 doTestTransactionWithTimeout(1);
846 }
847
848 private void doTestTransactionWithTimeout(int timeout) throws Exception {
849 Assume.group(TestGroup.PERFORMANCE);
850
851 PreparedStatement ps = mock(PreparedStatement.class);
852 given(con.getAutoCommit()).willReturn(true);
853 given(con.prepareStatement("some SQL statement")).willReturn(ps);
854
855 TransactionTemplate tt = new TransactionTemplate(tm);
856 tt.setTimeout(timeout);
857 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
858
859 try {
860 tt.execute(new TransactionCallbackWithoutResult() {
861 @Override
862 protected void doInTransactionWithoutResult(TransactionStatus status) {
863 try {
864 Thread.sleep(1500);
865 }
866 catch (InterruptedException ex) {
867 }
868 try {
869 Connection con = DataSourceUtils.getConnection(ds);
870 PreparedStatement ps = con.prepareStatement("some SQL statement");
871 DataSourceUtils.applyTransactionTimeout(ps, ds);
872 }
873 catch (SQLException ex) {
874 throw new DataAccessResourceFailureException("", ex);
875 }
876 }
877 });
878 if (timeout <= 1) {
879 fail("Should have thrown TransactionTimedOutException");
880 }
881 }
882 catch (TransactionTimedOutException ex) {
883 if (timeout <= 1) {
884
885 }
886 else {
887 throw ex;
888 }
889 }
890
891 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
892 if (timeout > 1) {
893 verify(ps).setQueryTimeout(timeout - 1);
894 verify(con).commit();
895 }
896 else {
897 verify(con).rollback();
898 }
899 InOrder ordered = inOrder(con);
900 ordered.verify(con).setAutoCommit(false);
901 ordered.verify(con).setAutoCommit(true);
902 verify(con).close();
903
904 }
905
906 @Test
907 public void testTransactionAwareDataSourceProxy() throws Exception {
908 given(con.getAutoCommit()).willReturn(true);
909
910 TransactionTemplate tt = new TransactionTemplate(tm);
911 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
912 tt.execute(new TransactionCallbackWithoutResult() {
913 @Override
914 protected void doInTransactionWithoutResult(TransactionStatus status) {
915
916 assertEquals(con, DataSourceUtils.getConnection(ds));
917 TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds);
918 try {
919 assertEquals(con, ((ConnectionProxy) dsProxy.getConnection()).getTargetConnection());
920 assertEquals(con, new SimpleNativeJdbcExtractor().getNativeConnection(dsProxy.getConnection()));
921
922 dsProxy.getConnection().close();
923 }
924 catch (SQLException ex) {
925 throw new UncategorizedSQLException("", "", ex);
926 }
927 }
928 });
929
930 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
931 InOrder ordered = inOrder(con);
932 ordered.verify(con).setAutoCommit(false);
933 ordered.verify(con).commit();
934 ordered.verify(con).setAutoCommit(true);
935 verify(con).close();
936 }
937
938 @Test
939 public void testTransactionAwareDataSourceProxyWithSuspension() throws Exception {
940 given(con.getAutoCommit()).willReturn(true);
941
942 final TransactionTemplate tt = new TransactionTemplate(tm);
943 tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
944 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
945
946 tt.execute(new TransactionCallbackWithoutResult() {
947 @Override
948 protected void doInTransactionWithoutResult(TransactionStatus status) {
949
950 assertEquals(con, DataSourceUtils.getConnection(ds));
951 final TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds);
952 try {
953 assertEquals(con, ((ConnectionProxy) dsProxy.getConnection()).getTargetConnection());
954 assertEquals(con, new SimpleNativeJdbcExtractor().getNativeConnection(dsProxy.getConnection()));
955
956 dsProxy.getConnection().close();
957 }
958 catch (SQLException ex) {
959 throw new UncategorizedSQLException("", "", ex);
960 }
961
962 tt.execute(new TransactionCallbackWithoutResult() {
963 @Override
964 protected void doInTransactionWithoutResult(TransactionStatus status) {
965
966 assertEquals(con, DataSourceUtils.getConnection(ds));
967 try {
968 assertEquals(con, ((ConnectionProxy) dsProxy.getConnection()).getTargetConnection());
969 assertEquals(con, new SimpleNativeJdbcExtractor().getNativeConnection(dsProxy.getConnection()));
970
971 dsProxy.getConnection().close();
972 }
973 catch (SQLException ex) {
974 throw new UncategorizedSQLException("", "", ex);
975 }
976 }
977 });
978
979 try {
980 assertEquals(con, ((ConnectionProxy) dsProxy.getConnection()).getTargetConnection());
981
982 dsProxy.getConnection().close();
983 }
984 catch (SQLException ex) {
985 throw new UncategorizedSQLException("", "", ex);
986 }
987 }
988 });
989
990 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
991 InOrder ordered = inOrder(con);
992 ordered.verify(con).setAutoCommit(false);
993 ordered.verify(con).commit();
994 ordered.verify(con).setAutoCommit(true);
995 verify(con, times(2)).close();
996 }
997
998 @Test
999 public void testTransactionAwareDataSourceProxyWithSuspensionAndReobtaining() throws Exception {
1000 given(con.getAutoCommit()).willReturn(true);
1001
1002 final TransactionTemplate tt = new TransactionTemplate(tm);
1003 tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
1004 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1005
1006 tt.execute(new TransactionCallbackWithoutResult() {
1007 @Override
1008 protected void doInTransactionWithoutResult(TransactionStatus status) {
1009
1010 assertEquals(con, DataSourceUtils.getConnection(ds));
1011 final TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds);
1012 dsProxy.setReobtainTransactionalConnections(true);
1013 try {
1014 assertEquals(con, ((ConnectionProxy) dsProxy.getConnection()).getTargetConnection());
1015 assertEquals(con, new SimpleNativeJdbcExtractor().getNativeConnection(dsProxy.getConnection()));
1016
1017 dsProxy.getConnection().close();
1018 }
1019 catch (SQLException ex) {
1020 throw new UncategorizedSQLException("", "", ex);
1021 }
1022
1023 tt.execute(new TransactionCallbackWithoutResult() {
1024 @Override
1025 protected void doInTransactionWithoutResult(TransactionStatus status) {
1026
1027 assertEquals(con, DataSourceUtils.getConnection(ds));
1028 try {
1029 assertEquals(con, ((ConnectionProxy) dsProxy.getConnection()).getTargetConnection());
1030 assertEquals(con, new SimpleNativeJdbcExtractor().getNativeConnection(dsProxy.getConnection()));
1031
1032 dsProxy.getConnection().close();
1033 }
1034 catch (SQLException ex) {
1035 throw new UncategorizedSQLException("", "", ex);
1036 }
1037 }
1038 });
1039
1040 try {
1041 assertEquals(con, ((ConnectionProxy) dsProxy.getConnection()).getTargetConnection());
1042
1043 dsProxy.getConnection().close();
1044 }
1045 catch (SQLException ex) {
1046 throw new UncategorizedSQLException("", "", ex);
1047 }
1048 }
1049 });
1050
1051 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1052 InOrder ordered = inOrder(con);
1053 ordered.verify(con).setAutoCommit(false);
1054 ordered.verify(con).commit();
1055 ordered.verify(con).setAutoCommit(true);
1056 verify(con, times(2)).close();
1057 }
1058
1059
1060
1061
1062 @Test
1063 public void testTransactionWithExceptionOnBegin() throws Exception {
1064 willThrow(new SQLException("Cannot begin")).given(con).getAutoCommit();
1065
1066 TransactionTemplate tt = new TransactionTemplate(tm);
1067 try {
1068 tt.execute(new TransactionCallbackWithoutResult() {
1069 @Override
1070 protected void doInTransactionWithoutResult(TransactionStatus status) {
1071
1072 }
1073 });
1074 fail("Should have thrown CannotCreateTransactionException");
1075 }
1076 catch (CannotCreateTransactionException ex) {
1077
1078 }
1079
1080 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1081 verify(con).close();
1082 }
1083
1084 @Test
1085 public void testTransactionWithExceptionOnCommit() throws Exception {
1086 willThrow(new SQLException("Cannot commit")).given(con).commit();
1087
1088 TransactionTemplate tt = new TransactionTemplate(tm);
1089 try {
1090 tt.execute(new TransactionCallbackWithoutResult() {
1091 @Override
1092 protected void doInTransactionWithoutResult(TransactionStatus status) {
1093
1094 }
1095 });
1096 fail("Should have thrown TransactionSystemException");
1097 }
1098 catch (TransactionSystemException ex) {
1099
1100 }
1101
1102 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1103 verify(con).close();
1104 }
1105
1106 @Test
1107 public void testTransactionWithExceptionOnCommitAndRollbackOnCommitFailure() throws Exception {
1108 willThrow(new SQLException("Cannot commit")).given(con).commit();
1109
1110 tm.setRollbackOnCommitFailure(true);
1111 TransactionTemplate tt = new TransactionTemplate(tm);
1112 try {
1113 tt.execute(new TransactionCallbackWithoutResult() {
1114 @Override
1115 protected void doInTransactionWithoutResult(TransactionStatus status) {
1116
1117 }
1118 });
1119 fail("Should have thrown TransactionSystemException");
1120 }
1121 catch (TransactionSystemException ex) {
1122
1123 }
1124
1125 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1126 verify(con).rollback();
1127 verify(con).close();
1128 }
1129
1130 @Test
1131 public void testTransactionWithExceptionOnRollback() throws Exception {
1132 given(con.getAutoCommit()).willReturn(true);
1133 willThrow(new SQLException("Cannot rollback")).given(con).rollback();
1134
1135 TransactionTemplate tt = new TransactionTemplate(tm);
1136 try {
1137 tt.execute(new TransactionCallbackWithoutResult() {
1138 @Override
1139 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
1140 status.setRollbackOnly();
1141 }
1142 });
1143 fail("Should have thrown TransactionSystemException");
1144 }
1145 catch (TransactionSystemException ex) {
1146
1147 }
1148
1149 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1150 InOrder ordered = inOrder(con);
1151 ordered.verify(con).setAutoCommit(false);
1152 ordered.verify(con).rollback();
1153 ordered.verify(con).setAutoCommit(true);
1154 verify(con).close();
1155 }
1156
1157 @Test
1158 public void testTransactionWithPropagationSupports() throws Exception {
1159 TransactionTemplate tt = new TransactionTemplate(tm);
1160 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
1161 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1162
1163 tt.execute(new TransactionCallbackWithoutResult() {
1164 @Override
1165 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
1166 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1167 assertTrue("Is not new transaction", !status.isNewTransaction());
1168 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
1169 assertFalse(TransactionSynchronizationManager.isActualTransactionActive());
1170 }
1171 });
1172
1173 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1174 }
1175
1176 @Test public void testTransactionWithPropagationNotSupported() throws Exception {
1177 TransactionTemplate tt = new TransactionTemplate(tm);
1178 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
1179 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1180
1181 tt.execute(new TransactionCallbackWithoutResult() {
1182 @Override
1183 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
1184 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1185 assertTrue("Is not new transaction", !status.isNewTransaction());
1186 }
1187 });
1188
1189 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1190 }
1191
1192 @Test
1193 public void testTransactionWithPropagationNever() throws Exception {
1194 TransactionTemplate tt = new TransactionTemplate(tm);
1195 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NEVER);
1196 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1197
1198 tt.execute(new TransactionCallbackWithoutResult() {
1199 @Override
1200 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
1201 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1202 assertTrue("Is not new transaction", !status.isNewTransaction());
1203 }
1204 });
1205
1206 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1207 }
1208
1209 @Test
1210 public void testExistingTransactionWithPropagationNested() throws Exception {
1211 doTestExistingTransactionWithPropagationNested(1);
1212 }
1213
1214 @Test
1215 public void testExistingTransactionWithPropagationNestedTwice() throws Exception {
1216 doTestExistingTransactionWithPropagationNested(2);
1217 }
1218
1219 private void doTestExistingTransactionWithPropagationNested(final int count) throws Exception {
1220 DatabaseMetaData md = mock(DatabaseMetaData.class);
1221 Savepoint sp = mock(Savepoint.class);
1222
1223 given(md.supportsSavepoints()).willReturn(true);
1224 given(con.getMetaData()).willReturn(md);
1225 for (int i = 1; i <= count; i++) {
1226 given(con.setSavepoint(ConnectionHolder.SAVEPOINT_NAME_PREFIX + i)).willReturn(sp);
1227 }
1228
1229 final TransactionTemplate tt = new TransactionTemplate(tm);
1230 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
1231 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1232 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
1233
1234 tt.execute(new TransactionCallbackWithoutResult() {
1235 @Override
1236 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
1237 assertTrue("Is new transaction", status.isNewTransaction());
1238 assertTrue("Isn't nested transaction", !status.hasSavepoint());
1239 for (int i = 0; i < count; i++) {
1240 tt.execute(new TransactionCallbackWithoutResult() {
1241 @Override
1242 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
1243 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
1244 assertTrue("Synchronization active", TransactionSynchronizationManager.isSynchronizationActive());
1245 assertTrue("Isn't new transaction", !status.isNewTransaction());
1246 assertTrue("Is nested transaction", status.hasSavepoint());
1247 }
1248 });
1249 }
1250 assertTrue("Is new transaction", status.isNewTransaction());
1251 assertTrue("Isn't nested transaction", !status.hasSavepoint());
1252 }
1253 });
1254
1255 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1256 verify(con, times(count)).releaseSavepoint(sp);
1257 verify(con).commit();
1258 verify(con).close();
1259 }
1260
1261 @Test
1262 public void testExistingTransactionWithPropagationNestedAndRollback() throws Exception {
1263 DatabaseMetaData md = mock(DatabaseMetaData.class);
1264 Savepoint sp = mock(Savepoint.class);
1265
1266 given(md.supportsSavepoints()).willReturn(true);
1267 given(con.getMetaData()).willReturn(md);
1268 given(con.setSavepoint("SAVEPOINT_1")).willReturn(sp);
1269
1270 final TransactionTemplate tt = new TransactionTemplate(tm);
1271 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
1272 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1273 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
1274
1275 tt.execute(new TransactionCallbackWithoutResult() {
1276 @Override
1277 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
1278 assertTrue("Is new transaction", status.isNewTransaction());
1279 assertTrue("Isn't nested transaction", !status.hasSavepoint());
1280 tt.execute(new TransactionCallbackWithoutResult() {
1281 @Override
1282 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
1283 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
1284 assertTrue("Synchronization active", TransactionSynchronizationManager.isSynchronizationActive());
1285 assertTrue("Isn't new transaction", !status.isNewTransaction());
1286 assertTrue("Is nested transaction", status.hasSavepoint());
1287 status.setRollbackOnly();
1288 }
1289 });
1290 assertTrue("Is new transaction", status.isNewTransaction());
1291 assertTrue("Isn't nested transaction", !status.hasSavepoint());
1292 }
1293 });
1294
1295 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1296 verify(con).rollback(sp);
1297 verify(con).releaseSavepoint(sp);
1298 verify(con).commit();
1299 verify(con).isReadOnly();
1300 verify(con).close();
1301 }
1302
1303 @Test
1304 public void testExistingTransactionWithManualSavepoint() throws Exception {
1305 DatabaseMetaData md = mock(DatabaseMetaData.class);
1306 Savepoint sp = mock(Savepoint.class);
1307
1308 given(md.supportsSavepoints()).willReturn(true);
1309 given(con.getMetaData()).willReturn(md);
1310 given(con.setSavepoint("SAVEPOINT_1")).willReturn(sp);
1311
1312 final TransactionTemplate tt = new TransactionTemplate(tm);
1313 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
1314 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1315 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
1316
1317 tt.execute(new TransactionCallbackWithoutResult() {
1318 @Override
1319 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
1320 assertTrue("Is new transaction", status.isNewTransaction());
1321 Object savepoint = status.createSavepoint();
1322 status.releaseSavepoint(savepoint);
1323 assertTrue("Is new transaction", status.isNewTransaction());
1324 }
1325 });
1326
1327 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1328 verify(con).releaseSavepoint(sp);
1329 verify(con).commit();
1330 verify(con).close();
1331 verify(ds).getConnection();
1332 }
1333
1334 @Test
1335 public void testExistingTransactionWithManualSavepointAndRollback() throws Exception {
1336 DatabaseMetaData md = mock(DatabaseMetaData.class);
1337 Savepoint sp = mock(Savepoint.class);
1338
1339 given(md.supportsSavepoints()).willReturn(true);
1340 given(con.getMetaData()).willReturn(md);
1341 given(con.setSavepoint("SAVEPOINT_1")).willReturn(sp);
1342
1343 final TransactionTemplate tt = new TransactionTemplate(tm);
1344 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
1345 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1346 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
1347
1348 tt.execute(new TransactionCallbackWithoutResult() {
1349 @Override
1350 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
1351 assertTrue("Is new transaction", status.isNewTransaction());
1352 Object savepoint = status.createSavepoint();
1353 status.rollbackToSavepoint(savepoint);
1354 assertTrue("Is new transaction", status.isNewTransaction());
1355 }
1356 });
1357
1358 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1359 verify(con).rollback(sp);
1360 verify(con).commit();
1361 verify(con).close();
1362 }
1363
1364 @Test
1365 public void testTransactionWithPropagationNested() throws Exception {
1366 final TransactionTemplate tt = new TransactionTemplate(tm);
1367 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
1368 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1369 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
1370
1371 tt.execute(new TransactionCallbackWithoutResult() {
1372 @Override
1373 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
1374 assertTrue("Is new transaction", status.isNewTransaction());
1375 }
1376 });
1377
1378 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1379 verify(con).commit();
1380 verify(con).close();
1381 }
1382
1383 @Test
1384 public void testTransactionWithPropagationNestedAndRollback() throws Exception {
1385 final TransactionTemplate tt = new TransactionTemplate(tm);
1386 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
1387 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1388 assertTrue("Synchronization not active", !TransactionSynchronizationManager.isSynchronizationActive());
1389
1390 tt.execute(new TransactionCallbackWithoutResult() {
1391 @Override
1392 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
1393 assertTrue("Is new transaction", status.isNewTransaction());
1394 status.setRollbackOnly();
1395 }
1396 });
1397
1398 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
1399 verify(con).rollback();
1400 verify(con).close();
1401 }
1402
1403
1404 private static class TestTransactionSynchronization implements TransactionSynchronization {
1405
1406 private DataSource dataSource;
1407
1408 private int status;
1409
1410 public boolean beforeCommitCalled;
1411
1412 public boolean beforeCompletionCalled;
1413
1414 public boolean afterCommitCalled;
1415
1416 public boolean afterCompletionCalled;
1417
1418 public TestTransactionSynchronization(DataSource dataSource, int status) {
1419 this.dataSource = dataSource;
1420 this.status = status;
1421 }
1422
1423 @Override
1424 public void suspend() {
1425 }
1426
1427 @Override
1428 public void resume() {
1429 }
1430
1431 @Override
1432 public void flush() {
1433 }
1434
1435 @Override
1436 public void beforeCommit(boolean readOnly) {
1437 if (this.status != TransactionSynchronization.STATUS_COMMITTED) {
1438 fail("Should never be called");
1439 }
1440 assertFalse(this.beforeCommitCalled);
1441 this.beforeCommitCalled = true;
1442 }
1443
1444 @Override
1445 public void beforeCompletion() {
1446 assertFalse(this.beforeCompletionCalled);
1447 this.beforeCompletionCalled = true;
1448 }
1449
1450 @Override
1451 public void afterCommit() {
1452 if (this.status != TransactionSynchronization.STATUS_COMMITTED) {
1453 fail("Should never be called");
1454 }
1455 assertFalse(this.afterCommitCalled);
1456 this.afterCommitCalled = true;
1457 }
1458
1459 @Override
1460 public void afterCompletion(int status) {
1461 assertFalse(this.afterCompletionCalled);
1462 this.afterCompletionCalled = true;
1463 assertTrue(status == this.status);
1464 assertTrue(TransactionSynchronizationManager.hasResource(this.dataSource));
1465 }
1466 }
1467
1468 }