Skip to content

Commit

Permalink
Discuss JdbcTransactionManager vs DataSourceTransactionManager
Browse files Browse the repository at this point in the history
Includes doc update for 6.0 default exception translation etc.

Closes gh-30802
  • Loading branch information
jhoeller committed Jul 5, 2023
1 parent 2365581 commit c5771bc
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 85 deletions.
8 changes: 4 additions & 4 deletions framework-docs/modules/ROOT/pages/data-access/dao.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,10 @@ Kotlin::
----
======

The last example we show here is for typical JDBC support. You could have the
`DataSource` injected into an initialization method or a constructor, where you would create a
`JdbcTemplate` and other data access support classes (such as `SimpleJdbcCall` and others) by using
this `DataSource`. The following example autowires a `DataSource`:
The last example we show here is for typical JDBC support. You could have the `DataSource`
injected into an initialization method or a constructor, where you would create a `JdbcTemplate`
and other data access support classes (such as `SimpleJdbcCall` and others) by using this
`DataSource`. The following example autowires a `DataSource`:

[tabs]
======
Expand Down
10 changes: 5 additions & 5 deletions framework-docs/modules/ROOT/pages/data-access/jdbc/advanced.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ to the database.
[[jdbc-batch-classic]]
== Basic Batch Operations with `JdbcTemplate`

You accomplish `JdbcTemplate` batch processing by implementing two methods of a special
interface, `BatchPreparedStatementSetter`, and passing that implementation in as the second parameter
You accomplish `JdbcTemplate` batch processing by implementing two methods of a special interface,
`BatchPreparedStatementSetter`, and passing that implementation in as the second parameter
in your `batchUpdate` method call. You can use the `getBatchSize` method to provide the size of
the current batch. You can use the `setValues` method to set the values for the parameters of
the prepared statement. This method is called the number of times that you
specified in the `getBatchSize` call. The following example updates the `t_actor` table
based on entries in a list, and the entire list is used as the batch:
the prepared statement. This method is called the number of times that you specified in the
`getBatchSize` call. The following example updates the `t_actor` table based on entries in a list,
and the entire list is used as the batch:

[tabs]
======
Expand Down
64 changes: 41 additions & 23 deletions framework-docs/modules/ROOT/pages/data-access/jdbc/connections.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This section covers:
* xref:data-access/jdbc/connections.adoc#jdbc-SingleConnectionDataSource[Using `SingleConnectionDataSource`]
* xref:data-access/jdbc/connections.adoc#jdbc-DriverManagerDataSource[Using `DriverManagerDataSource`]
* xref:data-access/jdbc/connections.adoc#jdbc-TransactionAwareDataSourceProxy[Using `TransactionAwareDataSourceProxy`]
* xref:data-access/jdbc/connections.adoc#jdbc-DataSourceTransactionManager[Using `DataSourceTransactionManager`]
* xref:data-access/jdbc/connections.adoc#jdbc-DataSourceTransactionManager[Using `DataSourceTransactionManager` / `JdbcTransactionManager`]


[[jdbc-datasource]]
Expand Down Expand Up @@ -125,8 +125,12 @@ The following example shows C3P0 configuration:
== Using `DataSourceUtils`

The `DataSourceUtils` class is a convenient and powerful helper class that provides
`static` methods to obtain connections from JNDI and close connections if necessary. It
supports thread-bound connections with, for example, `DataSourceTransactionManager`.
`static` methods to obtain connections from JNDI and close connections if necessary.
It supports a thread-bound JDBC `Connection` with `DataSourceTransactionManager` but
also with `JtaTransactionManager` and `JpaTransactionManager`.

Note that `JdbcTemplate` implies `DataSourceUtils` connection access, using it
behind every JDBC operation, implicitly participating in an ongoing transaction.


[[jdbc-SmartDataSource]]
Expand Down Expand Up @@ -165,7 +169,6 @@ In contrast to `DriverManagerDataSource`, it reuses the same connection all the
avoiding excessive creation of physical connections.



[[jdbc-DriverManagerDataSource]]
== Using `DriverManagerDataSource`

Expand Down Expand Up @@ -201,29 +204,44 @@ javadoc for more details.


[[jdbc-DataSourceTransactionManager]]
== Using `DataSourceTransactionManager`
== Using `DataSourceTransactionManager` / `JdbcTransactionManager`

The `DataSourceTransactionManager` class is a `PlatformTransactionManager`
implementation for single JDBC data sources. It binds a JDBC connection from the
specified data source to the currently executing thread, potentially allowing for one
thread connection per data source.
implementation for a single JDBC `DataSource`. It binds a JDBC `Connection`
from the specified `DataSource` to the currently executing thread, potentially
allowing for one thread-bound `Connection` per `DataSource`.

Application code is required to retrieve the JDBC connection through
`DataSourceUtils.getConnection(DataSource)` instead of Jakarta EE's standard
Application code is required to retrieve the JDBC `Connection` through
`DataSourceUtils.getConnection(DataSource)` instead of Java EE's standard
`DataSource.getConnection`. It throws unchecked `org.springframework.dao` exceptions
instead of checked `SQLExceptions`. All framework classes (such as `JdbcTemplate`) use this
strategy implicitly. If not used with this transaction manager, the lookup strategy
behaves exactly like the common one. Thus, it can be used in any case.

The `DataSourceTransactionManager` class supports custom isolation levels and timeouts
that get applied as appropriate JDBC statement query timeouts. To support the latter,
application code must either use `JdbcTemplate` or call the
`DataSourceUtils.applyTransactionTimeout(..)` method for each created statement.

You can use this implementation instead of `JtaTransactionManager` in the single-resource
case, as it does not require the container to support JTA. Switching between
both is just a matter of configuration, provided you stick to the required connection lookup
pattern. JTA does not support custom isolation levels.
instead of checked `SQLExceptions`. All framework classes (such as `JdbcTemplate`) use
this strategy implicitly. If not used with a transaction manager, the lookup strategy
behaves exactly like `DataSource.getConnection` and can therefore be used in any case.

The `DataSourceTransactionManager` class supports savepoints (`PROPAGATION_NESTED`),
custom isolation levels, and timeouts that get applied as appropriate JDBC statement
query timeouts. To support the latter, application code must either use `JdbcTemplate` or
call the `DataSourceUtils.applyTransactionTimeout(..)` method for each created statement.

You can use `DataSourceTransactionManager` instead of `JtaTransactionManager` in the
single-resource case, as it does not require the container to support a JTA transaction
coordinator. Switching between these transaction managers is just a matter of configuration,
provided you stick to the required connection lookup pattern. Note that JTA does not support
savepoints or custom isolation levels and has a different timeout mechanism but otherwise
exposes similar behavior in terms of JDBC resources and JDBC commit/rollback management.

NOTE: As of 5.3, Spring provides an extended `JdbcTransactionManager` variant which adds
exception translation capabilities on commit/rollback (aligned with `JdbcTemplate`).
Where `DataSourceTransactionManager` will only ever throw `TransactionSystemException`
(analogous to JTA), `JdbcTransactionManager` translates database locking failures etc to
corresponding `DataAccessException` subclasses. Note that application code needs to be
prepared for such exceptions, not exclusively expecting `TransactionSystemException`.
In scenarios where that is the case, `JdbcTransactionManager` is the recommended choice.

In terms of exception behavior, `JdbcTransactionManager` is roughly equivalent to
`JpaTransactionManager` and also to `R2dbcTransactionManager`, serving as an immediate
companion/replacement for each other. `DataSourceTransactionManager` on the other hand
is equivalent to `JtaTransactionManager` and can serve as a direct replacement there.



36 changes: 22 additions & 14 deletions framework-docs/modules/ROOT/pages/data-access/jdbc/core.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -718,12 +718,22 @@ See also xref:data-access/jdbc/core.adoc#jdbc-JdbcTemplate-idioms[`JdbcTemplate`
between ``SQLException``s and Spring's own `org.springframework.dao.DataAccessException`,
which is agnostic in regard to data access strategy. Implementations can be generic (for
example, using SQLState codes for JDBC) or proprietary (for example, using Oracle error
codes) for greater precision.
codes) for greater precision. This exception translation mechanism is used behind the
the common `JdbcTemplate` and `JdbcTransactionManager` entry points which do not
propagate `SQLException` but rather `DataAccessException`.

NOTE: As of 6.0, the default exception translator is `SQLExceptionSubclassTranslator`,
detecting JDBC 4 `SQLException` subclasses with a few extra checks, and with a fallback
to `SQLState` introspection through `SQLStateSQLExceptionTranslator`. This is usually
sufficient for common database access and does not require vendor-specific detection.
For backwards compatibility, consider using `SQLErrorCodeSQLExceptionTranslator` as
described below, potentially with custom error code mappings.

`SQLErrorCodeSQLExceptionTranslator` is the implementation of `SQLExceptionTranslator`
that is used by default. This implementation uses specific vendor codes. It is more
precise than the `SQLState` implementation. The error code translations are based on
codes held in a JavaBean type class called `SQLErrorCodes`. This class is created and
that is used by default when a file named `sql-error-codes.xml` is present in the root
of the classpath. This implementation uses specific vendor codes. It is more precise than
`SQLState` or `SQLException` subclass translation. The error code translations are based
on codes held in a JavaBean type class called `SQLErrorCodes`. This class is created and
populated by an `SQLErrorCodesFactory`, which (as the name suggests) is a factory for
creating `SQLErrorCodes` based on the contents of a configuration file named
`sql-error-codes.xml`. This file is populated with vendor codes and based on the
Expand All @@ -744,8 +754,8 @@ The `SQLErrorCodeSQLExceptionTranslator` applies matching rules in the following
translator. If this translation is not available, the next fallback translator is
the `SQLStateSQLExceptionTranslator`.

NOTE: The `SQLErrorCodesFactory` is used by default to define `Error` codes and custom exception
translations. They are looked up in a file named `sql-error-codes.xml` from the
NOTE: The `SQLErrorCodesFactory` is used by default to define error codes and custom
exception translations. They are looked up in a file named `sql-error-codes.xml` from the
classpath, and the matching `SQLErrorCodes` instance is located based on the database
name from the database metadata of the database in use.

Expand Down Expand Up @@ -784,12 +794,12 @@ Kotlin::
----
======

In the preceding example, the specific error code (`-12345`) is translated, while other errors are
left to be translated by the default translator implementation. To use this custom
translator, you must pass it to the `JdbcTemplate` through the method
`setExceptionTranslator`, and you must use this `JdbcTemplate` for all of the data access
processing where this translator is needed. The following example shows how you can use this custom
translator:
In the preceding example, the specific error code (`-12345`) is translated while
other errors are left to be translated by the default translator implementation.
To use this custom translator, you must pass it to the `JdbcTemplate` through the
method `setExceptionTranslator`, and you must use this `JdbcTemplate` for all of the
data access processing where this translator is needed. The following example shows
how you can use this custom translator:

[tabs]
======
Expand All @@ -800,7 +810,6 @@ Java::
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
// create a JdbcTemplate and set data source
this.jdbcTemplate = new JdbcTemplate();
this.jdbcTemplate.setDataSource(dataSource);
Expand All @@ -809,7 +818,6 @@ Java::
CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator();
tr.setDataSource(dataSource);
this.jdbcTemplate.setExceptionTranslator(tr);
}
public void updateShippingCharge(long orderId, long pct) {
Expand Down
24 changes: 12 additions & 12 deletions framework-docs/modules/ROOT/pages/data-access/jdbc/packages.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,30 @@

The Spring Framework's JDBC abstraction framework consists of four different packages:

* `core`: The `org.springframework.jdbc.core` package contains the `JdbcTemplate` class and its
various callback interfaces, plus a variety of related classes. A subpackage named
`org.springframework.jdbc.core.simple` contains the `SimpleJdbcInsert` and
* `core`: The `org.springframework.jdbc.core` package contains the `JdbcTemplate` class
and its various callback interfaces, plus a variety of related classes. A subpackage
named `org.springframework.jdbc.core.simple` contains the `SimpleJdbcInsert` and
`SimpleJdbcCall` classes. Another subpackage named
`org.springframework.jdbc.core.namedparam` contains the `NamedParameterJdbcTemplate`
class and the related support classes. See xref:data-access/jdbc/core.adoc[Using the JDBC Core Classes to Control Basic JDBC Processing and Error Handling], xref:data-access/jdbc/advanced.adoc[JDBC Batch Operations], and
xref:data-access/jdbc/simple.adoc[Simplifying JDBC Operations with the `SimpleJdbc` Classes].

* `datasource`: The `org.springframework.jdbc.datasource` package contains a utility class for easy
`DataSource` access and various simple `DataSource` implementations that you can use for
testing and running unmodified JDBC code outside of a Jakarta EE container. A subpackage
named `org.springfamework.jdbc.datasource.embedded` provides support for creating
* `datasource`: The `org.springframework.jdbc.datasource` package contains a utility class
for easy `DataSource` access and various simple `DataSource` implementations that you can
use for testing and running unmodified JDBC code outside of a Jakarta EE container. A subpackage
named `org.springframework.jdbc.datasource.embedded` provides support for creating
embedded databases by using Java database engines, such as HSQL, H2, and Derby. See
xref:data-access/jdbc/connections.adoc[Controlling Database Connections] and xref:data-access/jdbc/embedded-database-support.adoc[Embedded Database Support].

* `object`: The `org.springframework.jdbc.object` package contains classes that represent RDBMS
queries, updates, and stored procedures as thread-safe, reusable objects. See
* `object`: The `org.springframework.jdbc.object` package contains classes that represent
RDBMS queries, updates, and stored procedures as thread-safe, reusable objects. See
xref:data-access/jdbc/object.adoc[Modeling JDBC Operations as Java Objects]. This approach is modeled by JDO, although objects returned by queries
are naturally disconnected from the database. This higher-level of JDBC abstraction
depends on the lower-level abstraction in the `org.springframework.jdbc.core` package.

* `support`: The `org.springframework.jdbc.support` package provides `SQLException` translation
functionality and some utility classes. Exceptions thrown during JDBC processing are
translated to exceptions defined in the `org.springframework.dao` package. This means
* `support`: The `org.springframework.jdbc.support` package provides `SQLException`
translation functionality and some utility classes. Exceptions thrown during JDBC processing
are translated to exceptions defined in the `org.springframework.dao` package. This means
that code using the Spring JDBC abstraction layer does not need to implement JDBC or
RDBMS-specific error handling. All translated exceptions are unchecked, which gives you
the option of catching the exceptions from which you can recover while letting other
Expand Down
2 changes: 1 addition & 1 deletion framework-docs/modules/ROOT/pages/data-access/orm/jpa.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ features supported by Spring, usually in a vendor-specific manner:
* Applying specific transaction semantics (such as custom isolation level or transaction
timeout)
* Retrieving the transactional JDBC `Connection` (for exposure to JDBC-based DAOs)
* Advanced translation of `PersistenceExceptions` to Spring `DataAccessExceptions`
* Advanced translation of `PersistenceException` to Spring's `DataAccessException`

This is particularly valuable for special transaction semantics and for advanced
translation of exception. The default implementation (`DefaultJpaDialect`) does
Expand Down
18 changes: 7 additions & 11 deletions framework-docs/modules/ROOT/pages/data-access/r2dbc.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -718,19 +718,15 @@ javadoc for more details.
=== Using `R2dbcTransactionManager`

The `R2dbcTransactionManager` class is a `ReactiveTransactionManager` implementation for
single R2DBC data sources. It binds an R2DBC connection from the specified connection factory
to the subscriber `Context`, potentially allowing for one subscriber connection for each
connection factory.
a single R2DBC `ConnectionFactory`. It binds an R2DBC `Connection` from the specified
`ConnectionFactory` to the subscriber `Context`, potentially allowing for one subscriber
`Connection` for each `ConnectionFactory`.

Application code is required to retrieve the R2DBC connection through
Application code is required to retrieve the R2DBC `Connection` through
`ConnectionFactoryUtils.getConnection(ConnectionFactory)`, instead of R2DBC's standard
`ConnectionFactory.create()`.

All framework classes (such as `DatabaseClient`) use this strategy implicitly.
If not used with this transaction manager, the lookup strategy behaves exactly like the common one.
Thus, it can be used in any case.

The `R2dbcTransactionManager` class supports custom isolation levels that get applied to the connection.
`ConnectionFactory.create()`. All framework classes (such as `DatabaseClient`) use this
strategy implicitly. If not used with a transaction manager, the lookup strategy behaves
exactly like `ConnectionFactory.create()` and can therefore be used in any case.



Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ In XML configuration, the `<tx:annotation-driven/>` tag provides similar conveni
----
<1> The line that makes the bean instance transactional.


TIP: You can omit the `transaction-manager` attribute in the `<tx:annotation-driven/>`
tag if the bean name of the `TransactionManager` that you want to wire in has the name
`transactionManager`. If the `TransactionManager` bean that you want to dependency-inject
Expand Down Expand Up @@ -522,17 +521,17 @@ The following listing shows the bean declarations:
----
<tx:annotation-driven/>
<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<bean id="transactionManager1" class="org.springframework.jdbc.support.JdbcTransactionManager">
...
<qualifier value="order"/>
</bean>
<bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<bean id="transactionManager2" class="org.springframework.jdbc.support.JdbcTransactionManager">
...
<qualifier value="account"/>
</bean>
<bean id="transactionManager3" class="org.springframework.data.r2dbc.connectionfactory.R2dbcTransactionManager">
<bean id="transactionManager3" class="org.springframework.data.r2dbc.connection.R2dbcTransactionManager">
...
<qualifier value="reactive-account"/>
</bean>
Expand Down

0 comments on commit c5771bc

Please sign in to comment.