It should now be clear how you create different transaction
managers, and how they are linked to related resources that need to be
synchronized to transactions (for example
DataSourceTransactionManager
to a JDBC
DataSource
,
HibernateTransactionManager
to a Hibernate
SessionFactory
, and so forth). This section
describes how the application code, directly or indirectly using a
persistence API such as JDBC, Hibernate, or JDO, ensures that these
resources are created, reused, and cleaned up properly. The section also
discusses how transaction synchronization is triggered (optionally)
through the relevant
PlatformTransactionManager
.
The preferred approach is to use Spring's highest level template
based persistence integration APIs or to use native ORM APIs with
transaction- aware factory beans or proxies for managing the native
resource factories. These transaction-aware solutions internally handle
resource creation and reuse, cleanup, optional transaction
synchronization of the resources, and exception mapping. Thus user data
access code does not have to address these tasks, but can be focused
purely on non-boilerplate persistence logic. Generally, you use the
native ORM API or take a template approach for JDBC
access by using the JdbcTemplate
. These solutions
are detailed in subsequent chapters of this reference documentation.
Classes such as DataSourceUtils
(for JDBC),
EntityManagerFactoryUtils
(for JPA),
SessionFactoryUtils
(for Hibernate),
PersistenceManagerFactoryUtils
(for JDO), and so
on exist at a lower level. When you want the application code to deal
directly with the resource types of the native persistence APIs, you use
these classes to ensure that proper Spring Framework-managed instances
are obtained, transactions are (optionally) synchronized, and exceptions
that occur in the process are properly mapped to a consistent
API.
For example, in the case of JDBC, instead of the traditional JDBC
approach of calling the getConnection()
method on the
DataSource
, you instead use Spring's
org.springframework.jdbc.datasource.DataSourceUtils
class as follows:
Connection conn = DataSourceUtils.getConnection(dataSource);
If an existing transaction already has a connection synchronized
(linked) to it, that instance is returned. Otherwise, the method call
triggers the creation of a new connection, which is (optionally)
synchronized to any existing transaction, and made available for
subsequent reuse in that same transaction. As mentioned, any
SQLException
is wrapped in a Spring
Framework
CannotGetJdbcConnectionException
, one of
the Spring Framework's hierarchy of unchecked DataAccessExceptions. This
approach gives you more information than can be obtained easily from the
SQLException
, and ensures portability
across databases, even across different persistence technologies.
This approach also works without Spring transaction management (transaction synchronization is optional), so you can use it whether or not you are using Spring for transaction management.
Of course, once you have used Spring's JDBC support, JPA support
or Hibernate support, you will generally prefer not to use
DataSourceUtils
or the other helper classes,
because you will be much happier working through the Spring abstraction
than directly with the relevant APIs. For example, if you use the Spring
JdbcTemplate
or jdbc.object
package to simplify your use of JDBC, correct connection retrieval
occurs behind the scenes and you won't need to write any special
code.
At the very lowest level exists the
TransactionAwareDataSourceProxy
class. This is a
proxy for a target DataSource
, which
wraps the target DataSource
to add
awareness of Spring-managed transactions. In this respect, it is similar
to a transactional JNDI DataSource
as
provided by a Java EE server.
It should almost never be necessary or desirable to use this
class, except when existing code must be called and passed a standard
JDBC DataSource
interface implementation.
In that case, it is possible that this code is usable, but participating
in Spring managed transactions. It is preferable to write your new code
by using the higher level abstractions mentioned above.