How to create various transaction managers and how they are associated with the corresponding resources that need to be synchronized with transactions (for example, DataSourceTransactionManager with DataSource from JDBC, HibernateTransactionManager with SessionFactory from Hibernate and so on) should be clear now. This section describes how application code (either directly or indirectly through a persistence API such as JDBC, Hibernate, or JPA) ensures that these resources are created, reused, and cleaned up properly. The section also discusses how transaction synchronization (optional) can be triggered through the appropriate TransactionManager.

High-level synchronization approach

The preferred approach is to use high-level template-based persistence integration APIs from Spring or use native ORM APIs with transaction-oriented factory beans or proxies to manage native resource factories. These transaction-oriented solutions internally handle resource creation and reuse, cleanup, optional resource synchronization with transactions, and exception conversion. This way, custom data access code does not need to handle these tasks, but can instead be refocused solely on non-stereotypical data persistence logic. Typically, you use the built-in ORM API or use a template-based approach to achieve JDBC access using JdbcTemplate. These software solutions are described in detail in subsequent sections of this reference documentation.

Low-level synchronization approach

Classes like DataSourceUtils (for JDBC), EntityManagerFactoryUtils (for JPA), SessionFactoryUtils (for Hibernate) and so on exist at a lower level. If you need your application code to work directly with the resource types of the native persistence APIs, then use these classes to ensure that proper Spring Framework-managed instances are retrieved, transactions are synchronized (optional), and exceptions thrown in the process are correctly displayed in a consistent API. interface.

For example, in the case of JDBC, instead of the traditional JDBC approach of calling the getConnection() method on the DataSource, you can use the org.springframework.jdbc.datasource.DataSourceUtils class from Spring as shown below:

Connection conn = DataSourceUtils.getConnection(dataSource);

If an existing transaction already has a connection synchronized (bound) to it, this instance is returned. Otherwise, calling the method results in the creation of a new connection, which is (optionally) synchronized with any existing transaction and made available for later reuse in the same transaction. As mentioned earlier, any SQLException exception is wrapped in a CannotGetJdbcConnectionException exception from Spring Framewor, which is one of the exceptions in the DataAccessException hierarchy of unchecked types in the Spring Framework. This approach provides more information that can be easily obtained from SQLException, and provides portability between databases and even between different persistent storage technologies.

This approach also works without Spring transaction management (transaction synchronization is optional), so you can use it whether you use Spring for transaction management or not.

Naturally, after using Spring's JDBC, JPA, or Hibernate support, it is usually preferable not to use DataSourceUtils or other helper classes, since you will be much happier working through the Spring abstraction than working directly with the associated APIs. interfaces. For example, if you use Spring's JdbcTemplate or the jdbc.object package to make JDBC easier to use, correct connection discovery happens behind the scenes without you having to write any special code.

TransactionAwareDataSourceProxy

At the lowest level there is the TransactionAwareDataSourceProxy class. This is a proxy for the target DataSource that wraps the target DataSource to improve the compatibility level of Spring-managed transactions. In this respect, it is similar to the JNDI transactional DataSource provided by the Java EE server.

You will almost never need or want to use this class, except when you need to call existing code and pass it a standard implementation of the DataSource interface from JDBC. In this case, it is possible that this code can be used, but it is involved in Spring-managed transactions. You can write new custom code using the higher-level abstractions mentioned earlier.