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.SQLException;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24 import javax.sql.DataSource;
25 import javax.transaction.RollbackException;
26 import javax.transaction.Status;
27 import javax.transaction.SystemException;
28 import javax.transaction.Transaction;
29 import javax.transaction.TransactionManager;
30 import javax.transaction.UserTransaction;
31
32 import org.junit.After;
33 import org.junit.Before;
34 import org.junit.Test;
35
36 import org.springframework.beans.factory.support.StaticListableBeanFactory;
37 import org.springframework.jdbc.datasource.lookup.BeanFactoryDataSourceLookup;
38 import org.springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter;
39 import org.springframework.transaction.TransactionDefinition;
40 import org.springframework.transaction.TransactionException;
41 import org.springframework.transaction.TransactionStatus;
42 import org.springframework.transaction.jta.JtaTransactionManager;
43 import org.springframework.transaction.jta.JtaTransactionObject;
44 import org.springframework.transaction.support.TransactionCallbackWithoutResult;
45 import org.springframework.transaction.support.TransactionSynchronization;
46 import org.springframework.transaction.support.TransactionSynchronizationManager;
47 import org.springframework.transaction.support.TransactionTemplate;
48
49 import static org.junit.Assert.*;
50 import static org.mockito.BDDMockito.*;
51
52
53
54
55
56 public class DataSourceJtaTransactionTests {
57
58 private Connection connection;
59 private DataSource dataSource;
60 private UserTransaction userTransaction;
61 private TransactionManager transactionManager;
62 private Transaction transaction;
63
64 @Before
65 public void setup() throws Exception {
66 connection =mock(Connection.class);
67 dataSource = mock(DataSource.class);
68 userTransaction = mock(UserTransaction.class);
69 transactionManager = mock(TransactionManager.class);
70 transaction = mock(Transaction.class);
71 given(dataSource.getConnection()).willReturn(connection);
72 }
73
74 @After
75 public void verifyTransactionSynchronizationManagerState() {
76 assertTrue(TransactionSynchronizationManager.getResourceMap().isEmpty());
77 assertFalse(TransactionSynchronizationManager.isSynchronizationActive());
78 assertNull(TransactionSynchronizationManager.getCurrentTransactionName());
79 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
80 assertNull(TransactionSynchronizationManager.getCurrentTransactionIsolationLevel());
81 assertFalse(TransactionSynchronizationManager.isActualTransactionActive());
82 }
83
84 @Test
85 public void testJtaTransactionCommit() throws Exception {
86 doTestJtaTransaction(false);
87 }
88
89 @Test
90 public void testJtaTransactionRollback() throws Exception {
91 doTestJtaTransaction(true);
92 }
93
94 private void doTestJtaTransaction(final boolean rollback) throws Exception {
95 if (rollback) {
96 given(userTransaction.getStatus()).willReturn(
97 Status.STATUS_NO_TRANSACTION,Status.STATUS_ACTIVE);
98 }
99 else {
100 given(userTransaction.getStatus()).willReturn(
101 Status.STATUS_NO_TRANSACTION, Status.STATUS_ACTIVE, Status.STATUS_ACTIVE);
102 }
103
104 JtaTransactionManager ptm = new JtaTransactionManager(userTransaction);
105 TransactionTemplate tt = new TransactionTemplate(ptm);
106 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(dataSource));
107 assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
108
109 tt.execute(new TransactionCallbackWithoutResult() {
110 @Override
111 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
112 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(dataSource));
113 assertTrue("JTA synchronizations active", TransactionSynchronizationManager.isSynchronizationActive());
114 assertTrue("Is new transaction", status.isNewTransaction());
115
116 Connection c = DataSourceUtils.getConnection(dataSource);
117 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dataSource));
118 DataSourceUtils.releaseConnection(c, dataSource);
119
120 c = DataSourceUtils.getConnection(dataSource);
121 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dataSource));
122 DataSourceUtils.releaseConnection(c, dataSource);
123
124 if (rollback) {
125 status.setRollbackOnly();
126 }
127 }
128 });
129
130 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(dataSource));
131 assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
132 verify(userTransaction).begin();
133 if (rollback) {
134 verify(userTransaction).rollback();
135 }
136 verify(connection).close();
137 }
138
139 @Test
140 public void testJtaTransactionCommitWithPropagationRequiresNew() throws Exception {
141 doTestJtaTransactionWithPropagationRequiresNew(false, false, false, false);
142 }
143
144 @Test
145 public void testJtaTransactionCommitWithPropagationRequiresNewWithAccessAfterResume() throws Exception {
146 doTestJtaTransactionWithPropagationRequiresNew(false, false, true, false);
147 }
148
149 @Test
150 public void testJtaTransactionCommitWithPropagationRequiresNewWithOpenOuterConnection() throws Exception {
151 doTestJtaTransactionWithPropagationRequiresNew(false, true, false, false);
152 }
153
154 @Test
155 public void testJtaTransactionCommitWithPropagationRequiresNewWithOpenOuterConnectionAccessed() throws Exception {
156 doTestJtaTransactionWithPropagationRequiresNew(false, true, true, false);
157 }
158
159 @Test
160 public void testJtaTransactionCommitWithPropagationRequiresNewWithTransactionAwareDataSource() throws Exception {
161 doTestJtaTransactionWithPropagationRequiresNew(false, false, true, true);
162 }
163
164 @Test
165 public void testJtaTransactionRollbackWithPropagationRequiresNew() throws Exception {
166 doTestJtaTransactionWithPropagationRequiresNew(true, false, false, false);
167 }
168
169 @Test
170 public void testJtaTransactionRollbackWithPropagationRequiresNewWithAccessAfterResume() throws Exception {
171 doTestJtaTransactionWithPropagationRequiresNew(true, false, true, false);
172 }
173
174 @Test
175 public void testJtaTransactionRollbackWithPropagationRequiresNewWithOpenOuterConnection() throws Exception {
176 doTestJtaTransactionWithPropagationRequiresNew(true, true, false, false);
177 }
178
179 @Test
180 public void testJtaTransactionRollbackWithPropagationRequiresNewWithOpenOuterConnectionAccessed() throws Exception {
181 doTestJtaTransactionWithPropagationRequiresNew(true, true, true, false);
182 }
183
184 @Test
185 public void testJtaTransactionRollbackWithPropagationRequiresNewWithTransactionAwareDataSource() throws Exception {
186 doTestJtaTransactionWithPropagationRequiresNew(true, false, true, true);
187 }
188
189 private void doTestJtaTransactionWithPropagationRequiresNew(
190 final boolean rollback, final boolean openOuterConnection, final boolean accessAfterResume,
191 final boolean useTransactionAwareDataSource) throws Exception {
192
193 given(transactionManager.suspend()).willReturn(transaction);
194 if (rollback) {
195 given(userTransaction.getStatus()).willReturn(Status.STATUS_NO_TRANSACTION,
196 Status.STATUS_ACTIVE);
197 }
198 else {
199 given(userTransaction.getStatus()).willReturn(Status.STATUS_NO_TRANSACTION,
200 Status.STATUS_ACTIVE, Status.STATUS_ACTIVE);
201 }
202
203 given(connection.isReadOnly()).willReturn(true);
204
205 final DataSource dsToUse = useTransactionAwareDataSource ?
206 new TransactionAwareDataSourceProxy(dataSource) : dataSource;
207
208 JtaTransactionManager ptm = new JtaTransactionManager(userTransaction, transactionManager);
209 final TransactionTemplate tt = new TransactionTemplate(ptm);
210 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
211 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(dsToUse));
212 assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
213
214 tt.execute(new TransactionCallbackWithoutResult() {
215 @Override
216 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
217 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(dsToUse));
218 assertTrue("JTA synchronizations active", TransactionSynchronizationManager.isSynchronizationActive());
219 assertTrue("Is new transaction", status.isNewTransaction());
220
221 Connection c = DataSourceUtils.getConnection(dsToUse);
222 try {
223 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dsToUse));
224 c.isReadOnly();
225 DataSourceUtils.releaseConnection(c, dsToUse);
226
227 c = DataSourceUtils.getConnection(dsToUse);
228 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dsToUse));
229 if (!openOuterConnection) {
230 DataSourceUtils.releaseConnection(c, dsToUse);
231 }
232 }
233 catch (SQLException ex) {
234 }
235
236 for (int i = 0; i < 5; i++) {
237
238 tt.execute(new TransactionCallbackWithoutResult() {
239 @Override
240 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
241 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(dsToUse));
242 assertTrue("JTA synchronizations active", TransactionSynchronizationManager.isSynchronizationActive());
243 assertTrue("Is new transaction", status.isNewTransaction());
244
245 try {
246 Connection c = DataSourceUtils.getConnection(dsToUse);
247 c.isReadOnly();
248 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dsToUse));
249 DataSourceUtils.releaseConnection(c, dsToUse);
250
251 c = DataSourceUtils.getConnection(dsToUse);
252 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dsToUse));
253 DataSourceUtils.releaseConnection(c, dsToUse);
254 }
255 catch (SQLException ex) {
256 }
257 }
258 });
259
260 }
261
262 if (rollback) {
263 status.setRollbackOnly();
264 }
265
266 if (accessAfterResume) {
267 try {
268 if (!openOuterConnection) {
269 c = DataSourceUtils.getConnection(dsToUse);
270 }
271 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dsToUse));
272 c.isReadOnly();
273 DataSourceUtils.releaseConnection(c, dsToUse);
274
275 c = DataSourceUtils.getConnection(dsToUse);
276 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dsToUse));
277 DataSourceUtils.releaseConnection(c, dsToUse);
278 }
279 catch (SQLException ex) {
280 }
281 }
282
283 else {
284 if (openOuterConnection) {
285 DataSourceUtils.releaseConnection(c, dsToUse);
286 }
287 }
288 }
289 });
290
291 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(dsToUse));
292 assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
293 verify(userTransaction, times(6)).begin();
294 verify(transactionManager, times(5)).resume(transaction);
295 if(rollback) {
296 verify(userTransaction, times(5)).commit();
297 verify(userTransaction).rollback();
298 } else {
299 verify(userTransaction, times(6)).commit();
300 }
301 if(accessAfterResume && !openOuterConnection) {
302 verify(connection, times(7)).close();
303 }
304 else {
305 verify(connection, times(6)).close();
306 }
307 }
308
309 @Test
310 public void testJtaTransactionCommitWithPropagationRequiredWithinSupports() throws Exception {
311 doTestJtaTransactionCommitWithNewTransactionWithinEmptyTransaction(false, false);
312 }
313
314 @Test
315 public void testJtaTransactionCommitWithPropagationRequiredWithinNotSupported() throws Exception {
316 doTestJtaTransactionCommitWithNewTransactionWithinEmptyTransaction(false, true);
317 }
318
319 @Test
320 public void testJtaTransactionCommitWithPropagationRequiresNewWithinSupports() throws Exception {
321 doTestJtaTransactionCommitWithNewTransactionWithinEmptyTransaction(true, false);
322 }
323
324 @Test
325 public void testJtaTransactionCommitWithPropagationRequiresNewWithinNotSupported() throws Exception {
326 doTestJtaTransactionCommitWithNewTransactionWithinEmptyTransaction(true, true);
327 }
328
329 private void doTestJtaTransactionCommitWithNewTransactionWithinEmptyTransaction(
330 final boolean requiresNew, boolean notSupported) throws Exception {
331
332 if (notSupported) {
333 given(userTransaction.getStatus()).willReturn(
334 Status.STATUS_ACTIVE,
335 Status.STATUS_NO_TRANSACTION,
336 Status.STATUS_ACTIVE,
337 Status.STATUS_ACTIVE);
338 given(transactionManager.suspend()).willReturn(transaction);
339 }
340 else {
341 given(userTransaction.getStatus()).willReturn(
342 Status.STATUS_NO_TRANSACTION,
343 Status.STATUS_NO_TRANSACTION,
344 Status.STATUS_ACTIVE,
345 Status.STATUS_ACTIVE);
346 }
347
348 final DataSource dataSource = mock(DataSource.class);
349 final Connection connection1 = mock(Connection.class);
350 final Connection connection2 = mock(Connection.class);
351 given(dataSource.getConnection()).willReturn(connection1, connection2);
352
353 final JtaTransactionManager ptm = new JtaTransactionManager(userTransaction, transactionManager);
354 TransactionTemplate tt = new TransactionTemplate(ptm);
355 tt.setPropagationBehavior(notSupported ?
356 TransactionDefinition.PROPAGATION_NOT_SUPPORTED : TransactionDefinition.PROPAGATION_SUPPORTS);
357
358 assertFalse(TransactionSynchronizationManager.isSynchronizationActive());
359 tt.execute(new TransactionCallbackWithoutResult() {
360 @Override
361 protected void doInTransactionWithoutResult(TransactionStatus status) {
362 assertTrue(TransactionSynchronizationManager.isSynchronizationActive());
363 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
364 assertFalse(TransactionSynchronizationManager.isActualTransactionActive());
365 assertSame(connection1, DataSourceUtils.getConnection(dataSource));
366 assertSame(connection1, DataSourceUtils.getConnection(dataSource));
367
368 TransactionTemplate tt2 = new TransactionTemplate(ptm);
369 tt2.setPropagationBehavior(requiresNew ?
370 TransactionDefinition.PROPAGATION_REQUIRES_NEW : TransactionDefinition.PROPAGATION_REQUIRED);
371 tt2.execute(new TransactionCallbackWithoutResult() {
372 @Override
373 protected void doInTransactionWithoutResult(TransactionStatus status) {
374 assertTrue(TransactionSynchronizationManager.isSynchronizationActive());
375 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
376 assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
377 assertSame(connection2, DataSourceUtils.getConnection(dataSource));
378 assertSame(connection2, DataSourceUtils.getConnection(dataSource));
379 }
380 });
381
382 assertTrue(TransactionSynchronizationManager.isSynchronizationActive());
383 assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
384 assertFalse(TransactionSynchronizationManager.isActualTransactionActive());
385 assertSame(connection1, DataSourceUtils.getConnection(dataSource));
386 }
387 });
388 assertFalse(TransactionSynchronizationManager.isSynchronizationActive());
389 verify(userTransaction).begin();
390 verify(userTransaction).commit();
391 if (notSupported) {
392 verify(transactionManager).resume(transaction);
393 }
394 verify(connection2).close();
395 verify(connection1).close();
396 }
397
398 @Test
399 public void testJtaTransactionCommitWithPropagationRequiresNewAndSuspendException() throws Exception {
400 doTestJtaTransactionWithPropagationRequiresNewAndBeginException(true, false, false);
401 }
402
403 @Test
404 public void testJtaTransactionCommitWithPropagationRequiresNewWithOpenOuterConnectionAndSuspendException() throws Exception {
405 doTestJtaTransactionWithPropagationRequiresNewAndBeginException(true, true, false);
406 }
407
408 @Test
409 public void testJtaTransactionCommitWithPropagationRequiresNewWithTransactionAwareDataSourceAndSuspendException() throws Exception {
410 doTestJtaTransactionWithPropagationRequiresNewAndBeginException(true, false, true);
411 }
412
413 @Test
414 public void testJtaTransactionCommitWithPropagationRequiresNewWithOpenOuterConnectionAndTransactionAwareDataSourceAndSuspendException() throws Exception {
415 doTestJtaTransactionWithPropagationRequiresNewAndBeginException(true, true, true);
416 }
417
418 @Test
419 public void testJtaTransactionCommitWithPropagationRequiresNewAndBeginException() throws Exception {
420 doTestJtaTransactionWithPropagationRequiresNewAndBeginException(false, false, false);
421 }
422
423 @Test
424 public void testJtaTransactionCommitWithPropagationRequiresNewWithOpenOuterConnectionAndBeginException() throws Exception {
425 doTestJtaTransactionWithPropagationRequiresNewAndBeginException(false, true, false);
426 }
427
428 @Test
429 public void testJtaTransactionCommitWithPropagationRequiresNewWithOpenOuterConnectionAndTransactionAwareDataSourceAndBeginException() throws Exception {
430 doTestJtaTransactionWithPropagationRequiresNewAndBeginException(false, true, true);
431 }
432
433 @Test
434 public void testJtaTransactionCommitWithPropagationRequiresNewWithTransactionAwareDataSourceAndBeginException() throws Exception {
435 doTestJtaTransactionWithPropagationRequiresNewAndBeginException(false, false, true);
436 }
437
438 private void doTestJtaTransactionWithPropagationRequiresNewAndBeginException(boolean suspendException,
439 final boolean openOuterConnection, final boolean useTransactionAwareDataSource) throws Exception {
440
441 given(userTransaction.getStatus()).willReturn(
442 Status.STATUS_NO_TRANSACTION,
443 Status.STATUS_ACTIVE,
444 Status.STATUS_ACTIVE);
445 if (suspendException) {
446 given(transactionManager.suspend()).willThrow(new SystemException());
447 }
448 else {
449 given(transactionManager.suspend()).willReturn(transaction);
450 willThrow(new SystemException()).given(userTransaction).begin();
451 }
452
453 given(connection.isReadOnly()).willReturn(true);
454
455 final DataSource dsToUse = useTransactionAwareDataSource ?
456 new TransactionAwareDataSourceProxy(dataSource) : dataSource;
457 if (dsToUse instanceof TransactionAwareDataSourceProxy) {
458 ((TransactionAwareDataSourceProxy) dsToUse).setReobtainTransactionalConnections(true);
459 }
460
461 JtaTransactionManager ptm = new JtaTransactionManager(userTransaction, transactionManager);
462 final TransactionTemplate tt = new TransactionTemplate(ptm);
463 tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
464 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(dsToUse));
465 assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
466
467 try {
468 tt.execute(new TransactionCallbackWithoutResult() {
469 @Override
470 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
471 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(dsToUse));
472 assertTrue("JTA synchronizations active", TransactionSynchronizationManager.isSynchronizationActive());
473 assertTrue("Is new transaction", status.isNewTransaction());
474
475 Connection c = DataSourceUtils.getConnection(dsToUse);
476 try {
477 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dsToUse));
478 c.isReadOnly();
479 DataSourceUtils.releaseConnection(c, dsToUse);
480
481 c = DataSourceUtils.getConnection(dsToUse);
482 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dsToUse));
483 if (!openOuterConnection) {
484 DataSourceUtils.releaseConnection(c, dsToUse);
485 }
486 }
487 catch (SQLException ex) {
488 }
489
490 try {
491 tt.execute(new TransactionCallbackWithoutResult() {
492 @Override
493 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
494 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(dsToUse));
495 assertTrue("JTA synchronizations active", TransactionSynchronizationManager.isSynchronizationActive());
496 assertTrue("Is new transaction", status.isNewTransaction());
497
498 Connection c = DataSourceUtils.getConnection(dsToUse);
499 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dsToUse));
500 DataSourceUtils.releaseConnection(c, dsToUse);
501
502 c = DataSourceUtils.getConnection(dsToUse);
503 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dsToUse));
504 DataSourceUtils.releaseConnection(c, dsToUse);
505 }
506 });
507 }
508 finally {
509 if (openOuterConnection) {
510 try {
511 c.isReadOnly();
512 DataSourceUtils.releaseConnection(c, dsToUse);
513 }
514 catch (SQLException ex) {
515 }
516 }
517 }
518 }
519 });
520
521 fail("Should have thrown TransactionException");
522 }
523 catch (TransactionException ex) {
524
525 }
526
527 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(dsToUse));
528 assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
529
530 verify(userTransaction).begin();
531 if(suspendException) {
532 verify(userTransaction).rollback();
533 }
534
535 if (suspendException) {
536 verify(connection, atLeastOnce()).close();
537 }
538 else {
539 verify(connection, never()).close();
540 }
541 }
542
543 @Test
544 public void testJtaTransactionWithConnectionHolderStillBound() throws Exception {
545 @SuppressWarnings("serial")
546 JtaTransactionManager ptm = new JtaTransactionManager(userTransaction) {
547
548 @Override
549 protected void doRegisterAfterCompletionWithJtaTransaction(
550 JtaTransactionObject txObject,
551 final List<TransactionSynchronization> synchronizations)
552 throws RollbackException, SystemException {
553 Thread async = new Thread() {
554 @Override
555 public void run() {
556 invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_COMMITTED);
557 }
558 };
559 async.start();
560 try {
561 async.join();
562 }
563 catch (InterruptedException ex) {
564 ex.printStackTrace();
565 }
566 }
567 };
568 TransactionTemplate tt = new TransactionTemplate(ptm);
569 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(dataSource));
570 assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
571
572 given(userTransaction.getStatus()).willReturn(Status.STATUS_ACTIVE);
573 for (int i = 0; i < 3; i++) {
574 final boolean releaseCon = (i != 1);
575
576 tt.execute(new TransactionCallbackWithoutResult() {
577 @Override
578 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
579 assertTrue("JTA synchronizations active", TransactionSynchronizationManager.isSynchronizationActive());
580 assertTrue("Is existing transaction", !status.isNewTransaction());
581
582 Connection c = DataSourceUtils.getConnection(dataSource);
583 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dataSource));
584 DataSourceUtils.releaseConnection(c, dataSource);
585
586 c = DataSourceUtils.getConnection(dataSource);
587 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dataSource));
588 if (releaseCon) {
589 DataSourceUtils.releaseConnection(c, dataSource);
590 }
591 }
592 });
593
594 if (!releaseCon) {
595 assertTrue("Still has connection holder", TransactionSynchronizationManager.hasResource(dataSource));
596 }
597 else {
598 assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(dataSource));
599 }
600 assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
601 }
602 verify(connection, times(3)).close();
603 }
604
605 @Test
606 public void testJtaTransactionWithIsolationLevelDataSourceAdapter() throws Exception {
607 given(userTransaction.getStatus()).willReturn(
608 Status.STATUS_NO_TRANSACTION,
609 Status.STATUS_ACTIVE,
610 Status.STATUS_ACTIVE,
611 Status.STATUS_NO_TRANSACTION,
612 Status.STATUS_ACTIVE,
613 Status.STATUS_ACTIVE);
614
615 final IsolationLevelDataSourceAdapter dsToUse = new IsolationLevelDataSourceAdapter();
616 dsToUse.setTargetDataSource(dataSource);
617 dsToUse.afterPropertiesSet();
618
619 JtaTransactionManager ptm = new JtaTransactionManager(userTransaction);
620 ptm.setAllowCustomIsolationLevels(true);
621
622 TransactionTemplate tt = new TransactionTemplate(ptm);
623 tt.execute(new TransactionCallbackWithoutResult() {
624 @Override
625 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
626 Connection c = DataSourceUtils.getConnection(dsToUse);
627 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dsToUse));
628 assertSame(connection, c);
629 DataSourceUtils.releaseConnection(c, dsToUse);
630 }
631 });
632
633 tt.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
634 tt.setReadOnly(true);
635 tt.execute(new TransactionCallbackWithoutResult() {
636 @Override
637 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
638 Connection c = DataSourceUtils.getConnection(dsToUse);
639 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dsToUse));
640 assertSame(connection, c);
641 DataSourceUtils.releaseConnection(c, dsToUse);
642 }
643 });
644
645 verify(userTransaction, times(2)).begin();
646 verify(userTransaction, times(2)).commit();
647 verify(connection).setReadOnly(true);
648 verify(connection).setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
649 verify(connection, times(2)).close();
650 }
651
652 @Test
653 public void testJtaTransactionWithIsolationLevelDataSourceRouter() throws Exception {
654 doTestJtaTransactionWithIsolationLevelDataSourceRouter(false);
655 }
656
657 @Test
658 public void testJtaTransactionWithIsolationLevelDataSourceRouterWithDataSourceLookup() throws Exception {
659 doTestJtaTransactionWithIsolationLevelDataSourceRouter(true);
660 }
661
662 private void doTestJtaTransactionWithIsolationLevelDataSourceRouter(boolean dataSourceLookup) throws Exception {
663 given( userTransaction.getStatus()).willReturn(Status.STATUS_NO_TRANSACTION, Status.STATUS_ACTIVE, Status.STATUS_ACTIVE, Status.STATUS_NO_TRANSACTION, Status.STATUS_ACTIVE, Status.STATUS_ACTIVE);
664
665 final DataSource dataSource1 = mock(DataSource.class);
666 final Connection connection1 = mock(Connection.class);
667 given(dataSource1.getConnection()).willReturn(connection1);
668
669 final DataSource dataSource2 = mock(DataSource.class);
670 final Connection connection2 = mock(Connection.class);
671 given(dataSource2.getConnection()).willReturn(connection2);
672
673 final IsolationLevelDataSourceRouter dsToUse = new IsolationLevelDataSourceRouter();
674 Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
675 if (dataSourceLookup) {
676 targetDataSources.put("ISOLATION_REPEATABLE_READ", "ds2");
677 dsToUse.setDefaultTargetDataSource("ds1");
678 StaticListableBeanFactory beanFactory = new StaticListableBeanFactory();
679 beanFactory.addBean("ds1", dataSource1);
680 beanFactory.addBean("ds2", dataSource2);
681 dsToUse.setDataSourceLookup(new BeanFactoryDataSourceLookup(beanFactory));
682 }
683 else {
684 targetDataSources.put("ISOLATION_REPEATABLE_READ", dataSource2);
685 dsToUse.setDefaultTargetDataSource(dataSource1);
686 }
687 dsToUse.setTargetDataSources(targetDataSources);
688 dsToUse.afterPropertiesSet();
689
690 JtaTransactionManager ptm = new JtaTransactionManager(userTransaction);
691 ptm.setAllowCustomIsolationLevels(true);
692
693 TransactionTemplate tt = new TransactionTemplate(ptm);
694 tt.execute(new TransactionCallbackWithoutResult() {
695 @Override
696 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
697 Connection c = DataSourceUtils.getConnection(dsToUse);
698 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dsToUse));
699 assertSame(connection1, c);
700 DataSourceUtils.releaseConnection(c, dsToUse);
701 }
702 });
703
704 tt.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
705 tt.execute(new TransactionCallbackWithoutResult() {
706 @Override
707 protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
708 Connection c = DataSourceUtils.getConnection(dsToUse);
709 assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(dsToUse));
710 assertSame(connection2, c);
711 DataSourceUtils.releaseConnection(c, dsToUse);
712 }
713 });
714
715 verify(userTransaction, times(2)).begin();
716 verify(userTransaction, times(2)).commit();
717 verify(connection1).close();
718 verify(connection2).close();
719 }
720 }