|
19 | 19 | import java.io.IOException; |
20 | 20 | import java.io.ObjectInputStream; |
21 | 21 | import java.io.Serializable; |
| 22 | +import java.lang.reflect.InvocationTargetException; |
| 23 | +import java.lang.reflect.Method; |
22 | 24 | import java.util.List; |
23 | 25 | import java.util.Properties; |
24 | 26 |
|
|
52 | 54 | import org.springframework.transaction.support.DefaultTransactionStatus; |
53 | 55 | import org.springframework.transaction.support.TransactionSynchronization; |
54 | 56 | import org.springframework.util.Assert; |
| 57 | +import org.springframework.util.ClassUtils; |
| 58 | +import org.springframework.util.ReflectionUtils; |
55 | 59 | import org.springframework.util.StringUtils; |
56 | 60 |
|
57 | 61 | /** |
@@ -139,6 +143,10 @@ public class JtaTransactionManager extends AbstractPlatformTransactionManager |
139 | 143 | "java:comp/TransactionSynchronizationRegistry"; |
140 | 144 |
|
141 | 145 |
|
| 146 | + // JTA 2.1 UserTransaction#setReadOnly(boolean) method available? |
| 147 | + private static final @Nullable Method setReadOnlyMethod = |
| 148 | + ClassUtils.getMethodIfAvailable(UserTransaction.class, "setReadOnly", boolean.class); |
| 149 | + |
142 | 150 | private transient JndiTemplate jndiTemplate = new JndiTemplate(); |
143 | 151 |
|
144 | 152 | private transient @Nullable UserTransaction userTransaction; |
@@ -858,6 +866,12 @@ protected void doJtaBegin(JtaTransactionObject txObject, TransactionDefinition d |
858 | 866 | applyIsolationLevel(txObject, definition.getIsolationLevel()); |
859 | 867 | int timeout = determineTimeout(definition); |
860 | 868 | applyTimeout(txObject, timeout); |
| 869 | + |
| 870 | + if (definition.isReadOnly()) { |
| 871 | + setReadOnlyIfPossible(txObject.getUserTransaction(), true); |
| 872 | + txObject.resetReadOnly = true; |
| 873 | + } |
| 874 | + |
861 | 875 | txObject.getUserTransaction().begin(); |
862 | 876 | } |
863 | 877 |
|
@@ -904,6 +918,21 @@ protected void applyTimeout(JtaTransactionObject txObject, int timeout) throws S |
904 | 918 | } |
905 | 919 | } |
906 | 920 |
|
| 921 | + private void setReadOnlyIfPossible(UserTransaction ut, boolean readOnly) throws SystemException { |
| 922 | + if (setReadOnlyMethod != null) { |
| 923 | + try { |
| 924 | + setReadOnlyMethod.invoke(ut, readOnly); |
| 925 | + } |
| 926 | + catch (Exception ex) { |
| 927 | + if (ex instanceof InvocationTargetException ute && |
| 928 | + ute.getTargetException() instanceof SystemException se) { |
| 929 | + throw se; |
| 930 | + } |
| 931 | + ReflectionUtils.handleReflectionException(ex); |
| 932 | + } |
| 933 | + } |
| 934 | + } |
| 935 | + |
907 | 936 |
|
908 | 937 | @Override |
909 | 938 | protected Object doSuspend(Object transaction) { |
@@ -1161,6 +1190,14 @@ else if (getTransactionManager() != null) { |
1161 | 1190 | @Override |
1162 | 1191 | protected void doCleanupAfterCompletion(Object transaction) { |
1163 | 1192 | JtaTransactionObject txObject = (JtaTransactionObject) transaction; |
| 1193 | + if (txObject.resetReadOnly) { |
| 1194 | + try { |
| 1195 | + setReadOnlyIfPossible(txObject.getUserTransaction(), false); |
| 1196 | + } |
| 1197 | + catch (SystemException ex) { |
| 1198 | + logger.debug("Failed to reset read-only flag after after JTA completion", ex); |
| 1199 | + } |
| 1200 | + } |
1164 | 1201 | if (txObject.resetTransactionTimeout) { |
1165 | 1202 | try { |
1166 | 1203 | txObject.getUserTransaction().setTransactionTimeout(0); |
|
0 commit comments