Add early support for JTA 2.1 read-only attribute

Closes gh-35633
This commit is contained in:
Juergen Hoeller
2025-10-14 13:25:13 +02:00
parent 43c13ca043
commit 001fc8e585
3 changed files with 43 additions and 0 deletions
@@ -78,6 +78,10 @@ public class JtaTransactionAnnotationParser implements TransactionAnnotationPars
}
rbta.setRollbackRules(rollbackRules);
if (attributes.containsKey("readOnly")) { // JTA 2.1
rbta.setReadOnly(attributes.getBoolean("readOnly"));
}
return rbta;
}
@@ -19,6 +19,8 @@ package org.springframework.transaction.jta;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Properties;
@@ -52,6 +54,8 @@ import org.springframework.transaction.support.AbstractPlatformTransactionManage
import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
@@ -139,6 +143,10 @@ public class JtaTransactionManager extends AbstractPlatformTransactionManager
"java:comp/TransactionSynchronizationRegistry";
// JTA 2.1 UserTransaction#setReadOnly(boolean) method available?
private static final @Nullable Method setReadOnlyMethod =
ClassUtils.getMethodIfAvailable(UserTransaction.class, "setReadOnly", boolean.class);
private transient JndiTemplate jndiTemplate = new JndiTemplate();
private transient @Nullable UserTransaction userTransaction;
@@ -858,6 +866,12 @@ public class JtaTransactionManager extends AbstractPlatformTransactionManager
applyIsolationLevel(txObject, definition.getIsolationLevel());
int timeout = determineTimeout(definition);
applyTimeout(txObject, timeout);
if (definition.isReadOnly()) {
setReadOnlyIfPossible(txObject.getUserTransaction(), true);
txObject.resetReadOnly = true;
}
txObject.getUserTransaction().begin();
}
@@ -904,6 +918,21 @@ public class JtaTransactionManager extends AbstractPlatformTransactionManager
}
}
private void setReadOnlyIfPossible(UserTransaction ut, boolean readOnly) throws SystemException {
if (setReadOnlyMethod != null) {
try {
setReadOnlyMethod.invoke(ut, readOnly);
}
catch (Exception ex) {
if (ex instanceof InvocationTargetException ute &&
ute.getTargetException() instanceof SystemException se) {
throw se;
}
ReflectionUtils.handleReflectionException(ex);
}
}
}
@Override
protected Object doSuspend(Object transaction) {
@@ -1161,6 +1190,14 @@ public class JtaTransactionManager extends AbstractPlatformTransactionManager
@Override
protected void doCleanupAfterCompletion(Object transaction) {
JtaTransactionObject txObject = (JtaTransactionObject) transaction;
if (txObject.resetReadOnly) {
try {
setReadOnlyIfPossible(txObject.getUserTransaction(), false);
}
catch (SystemException ex) {
logger.debug("Failed to reset read-only flag after after JTA completion", ex);
}
}
if (txObject.resetTransactionTimeout) {
try {
txObject.getUserTransaction().setTransactionTimeout(0);
@@ -41,6 +41,8 @@ public class JtaTransactionObject implements SmartTransactionObject {
boolean resetTransactionTimeout = false;
boolean resetReadOnly = false;
/**
* Create a new JtaTransactionObject for the given JTA UserTransaction.