I would like to use one DataSource bean for a mix of these operations: 1. readOnly=true (implies autoCommit=true) 2. readOnly=false with autoCommit=false 3. readOnly=false with autoCommit=true
One option might be @Transactional
annotation supporting autoCommit
and readOnly
tags; it only has readOnly
tag today.
Another option might be two out-of-the-box PlatformTransactionManager
beans; where @Transactional
value
tag can be used pick DataSourceTransactionManager
(aka AutoCommitOffTransactionManager
) versus AutoCommitOnTransactionManager
(new).
I am open to other solutions too, these are just suggestions.
In general, autoCommit on or off is a convenience feature. Both of these examples are transactions.
SET AUTOCOMMIT=0;
BEGIN;
UPDATE table SET version=version+1 WHERE version=0;
COMMIT;
SET AUTOCOMMIT=1;
UPDATE table SET version=version+1 WHERE version=0;
Both examples are transactional, because all of the affected rows are updated atomically. If a connection pool parks connections with AUTOCOMMIT=1
, then the 2nd example only requires one round-trip to the database.
- For high latency connections, using
autoCommit=false
does 4 expensive round-trips, while usingautoCommit=true
only does 1 expensive round-trip to execute the one-statement UPDATE. Both are transactional, but one is much faster. - For using
@Version
inREAD COMMITTED
mode, the first transaction requires application code to useOptimisticLockingException
and Retryable due to competing version column updates. For the second transaction example, the DB would handle optimistic locking much more efficiently, and Retryable may not be needed at all.
I opened a Stack Exchange question, but the answers I got were not helpful.
https://stackoverflow.com/questions/78268584/how-to-mix-autocommit-true-and-autocommit-true-transactional-operations-in
In summary, I would like to see Spring support using a single DataSource bean with a mix of autoCommit=true
and autoCommit=false
in user applications. Setting autoCommit=false
at the DataSource
level doesn't work, because I would need to create two DataSource
beans, and I would end up with separate connection pools.
Comment From: jhoeller
I actually agree with the answers on StackOverflow here: @Transactional
demarcates a transaction boundary, and the only way to enforce a consistently bounded transaction there with JDBC is to use autoCommit=false
and an explicit commit
step, encompassing any number of SQL statements inbetween. For that purpose, it does not matter that autoCommit=true
effectively leads to single-statement transactions on the database side. Our notion of an application-demarcated transaction is a guaranteed boundary with a single commit/rollback step which we need to enforce against the database resource.
FWIW, @Transactional(readOnly=true)
does not imply autoCommit=true
. Quite on the contrary, the read-only flag is effectively like an isolation level in that it provides an execution hint for a larger transaction. For single-statement operations in auto-commit mode, the read-only flag usually does not have much effect at all. That said, Spring's @Transactional(readOnly=true)
can imply a wider effect in that it may also affect connection pool routing and Hibernate caching behavior, so it may be worth setting that hint in any case for a transaction boundary that is known to only perform read-only operations.
Overall, classic @Transactional
with its default propagation=REQUIRED
is only to be used with explicitly demarcated transactions by design. However, you may declare @Transactional(propagation=SUPPORTS)
for a transaction boundary that supports application-level resource reuse without an underlying transaction enforced on the resource itself. Such a SUPPORTS
transaction boundary would work well with an auto-commit connection, and it can also participate in a larger transaction when called from a larger @Transactional(propagation=REQUIRED)
boundary.
Alternatively, you may also not declare any @Transactional
boundary for such single-statement operations at all. Simply let JdbcTemplate
and co execute their statements ad-hoc against a DataSource
that is set up with autoCommit=true
. The same DataSource
can then participate in @Transactional
-managed transactions still, incurring the auto-commit switch overhead for such explicitly demarcated transactions only.
So if all you want is an ad-hoc auto-commit transaction for certain data access operations, a hypothetical @Transactional(autoCommit=true)
would be roughly equivalent to the existing @Transactional(propagation=SUPPORTS)
. And in many scenarios, it would effectively be equivalent to no @Transactional
demarcation at all.