Spring provides integration with Hibernate, JDO, Oracle TopLink, Apache OJB and iBATIS SQL Maps: in terms of resource management, DAO implementation support, and transaction strategies. For example for Hibernate, there is first-class support with lots of IoC convenience features, addressing many typical Hibernate integration issues. All of these support packages for O/R mappers comply with Spring's generic transaction and DAO exception hierarchies. There are usually two integration styles: either using Spring's DAO 'templates' or coding DAOs against plain Hibernate/JDO/TopLink/etc APIs. In both cases, DAOs can be configured through Dependency Injection and participate in Spring's resource and transaction management.
Spring's adds significant support when using the O/R mapping layer of your choice to create data access applications. First of all, you should know that once you started using Spring's support for O/R mapping, you don't have to go all the way. No matter to what extent, you're invited to review and leverage the Spring approach, before deciding to take the effort and risk of building a similar infrastructure in-house. Much of the O/R mapping support, no matter what technology you're using may be used in a library style, as everything is designed as a set of reusable JavaBeans. Usage inside an ApplicationContext does provide additional benefits in terms of ease of configuration and deployment; as such, most examples in this section show configuration inside an ApplicationContext.
Some of the the benefits of using Spring to create your O/R mapping DAOs include:
Ease of testing. Spring's inversion of control approach makes it easy to swap the implementations and config locations of Hibernate SessionFactory instances, JDBC DataSources, transaction managers, and mapper object implementations (if needed). This makes it much easier to isolate and test each piece of persistence-related code in isolation.
Common data access exceptions.Spring can wrap exceptions from you O/R mapping tool of choice, converting them from proprietary (potentially checked) exceptions to a common runtime DataAccessException hierarchy. This allows you to handle most persistence exceptions, which are non-recoverable, only in the appropriate layers, without annoying boilerplate catches/throws, and exception declarations. You can still trap and handle exceptions anywhere you need to. Remember that JDBC exceptions (including DB specific dialects) are also converted to the same hierarchy, meaning that you can perform some operations with JDBC within a consistent programming model.
General resource management. Spring application contexts can handle the location and configuration of Hibernate SessionFactory instances, JDBC DataSources, iBATIS SQL Maps configuration objects, and other related resources. This makes these values easy to manage and change. Spring offers efficient, easy and safe handling of persistence resources. For example: Related code using Hibernate generally needs to use the same Hibernate Session for efficiency and proper transaction handling. Spring makes it easy to transparently create and bind a Session to the current thread, either by using an explicit 'template' wrapper class at the Java code level or by exposing a current Session through the Hibernate SessionFactory (for DAOs based on plain Hibernate3 API). Thus Spring solves many of the issues that repeatedly arise from typical Hibernate usage, for any transaction environment (local or JTA).
Integrated transaction management. Spring allows you to wrap your O/R mapping code with either a declarative, AOP style method interceptor, or an explicit 'template' wrapper class at the Java code level. In either case, transaction semantics are handled for you, and proper transaction handling (rollback, etc) in case of exceptions is taken care of. As discussed below, you also get the benefit of being able to use and swap various transaction managers, without your Hibernate/JDO related code being affected: for example, between local transactions and JTA, with the same full services (such as declarative transactions) available in both scenarios. As an additional benefit, JDBC-related code can fully integrate transactionally with the code you use to do O/R mapping. This is useful for data access that's not suitable for O/R mapping, such as batch processing or streaming of BLOBs, which still needs to share common transactions with O/R mapping operations.
To avoid vendor lock-in, and allow mix-and-match implementation strategies. While Hibernate is powerful, flexible, open source and free, it still uses a proprietary API. Furthermore one could argue that iBATIS is a bit lightweight, although it's excellent for use in application that don't require complex O/R mapping strategies. Given the choice, it's usually desirable to implement major application functionality using standard or abstracted APIs, in case you need to switch to another implementation for reasons of functionality, performance, or any other concerns. For example, Spring's abstraction of Hibernate transactions and exceptions, along with its IoC approach which allows you to easily swap in mapper/DAO objects implementing data access functionality, makes it easy to isolate all Hibernate-specific code in one area of your application, without sacrificing any of the power of Hibernate. Higher level service code dealing with the DAOs has no need to know anything about their implementation. This approach has the additional benefit of making it easy to intentionally implement data access with a mix-and-match approach (i.e. some data access performed using Hibernate, and some using JDBC, others using iBATIS) in a non-intrusive fashion, potentially providing great benefits in terms of continuing to use legacy code or leveraging the strength of each technology.
The PetClinic sample in the Spring distribution offers alternative DAO implementations and application context configurations for JDBC, Hibernate, Oracle TopLink, and Apache OJB. PetClinic can therefore serve as working sample app that illustrates the use of Hibernate, TopLink and OJB in a Spring web application. It also leverages declarative transaction demarcation with different transaction strategies.
The JPetStore sample illustrates the use of iBATIS SQL Maps in a Spring environment. It also features two web tier versions: one based on Spring Web MVC, one based on Struts.
Beyond the samples shipped with Spring, there is a variety of Spring-based O/R mapping samples provided by specific vendors: for example, the JDO implementations JPOX (http://www.jpox.org) and Kodo (http://www.solarmetric.com).
We will start with a coverage of Hibernate (http://www.hibernate.org) in a Spring environment, using it to demonstrate the approach that Spring takes towards integrating O/R mappers. This section will cover many issues in detail and show different variations of DAO implementations and transaction demarcations. Most of these patterns can be directly translated to all other supported O/R mapping tools. The following sections in this chapter will then cover the other O/R mappers, showing briefer examples there.
The following discussion focuses on "classic" Hibernate: that is, Hibernate 2.1, which has been supported in Spring since its inception. All of this can be applied to Hibernate 3.0 as-is, using the analogous Hibernate 3 support package introduced in Spring 1.2 final: org.springframework.orm.hibernate3, mirroring org.springframework.orm.hibernate with analogous support classes for Hibernate 3. Furthermore, all references to the net.sf.hibernate package need to be replaced with org.hibernate, following the root package change in Hibernate 3. Simply adapt the package names (as used in the examples) accordingly.
Typical business applications are often cluttered with repetitive resource management code. Many projects try to invent their own solutions for this issue, sometimes sacrificing proper handling of failures for programming convenience. Spring advocates strikingly simple solutions for proper resource handling: Inversion of control via templating, i.e. infrastructure classes with callback interfaces, or applying AOP interceptors. The infrastructure cares for proper resource handling, and for appropriate conversion of specific API exceptions to an unchecked infrastructure exception hierarchy. Spring introduces a DAO exception hierarchy, applicable to any data access strategy. For direct JDBC, the JdbcTemplate class mentioned in a previous section cares for connection handling, and for proper conversion of SQLException to the DataAccessException hierarchy, including translation of database-specific SQL error codes to meaningful exception classes. It supports both JTA and JDBC transactions, via respective Spring transaction managers.
Spring also offers Hibernate and JDO support, consisting of a HibernateTemplate / JdoTemplate analogous to JdbcTemplate, a HibernateInterceptor / JdoInterceptor, and a Hibernate / JDO transaction manager. The major goal is to allow for clear application layering, with any data access and transaction technology, and for loose coupling of application objects. No more business service dependencies on the data access or transaction strategy, no more hard-coded resource lookups, no more hard-to-replace singletons, no more custom service registries. One simple and consistent approach to wiring up application objects, keeping them as reusable and free from container dependencies as possible. All the individual data access features are usable on their own but integrate nicely with Spring's application context concept, providing XML-based configuration and cross-referencing of plain JavaBean instances that don't need to be Spring-aware. In a typical Spring app, many important objects are JavaBeans: data access templates, data access objects (that use the templates), transaction managers, business services (that use the data access objects and transaction managers), web view resolvers, web controllers (that use the business services), etc.
To avoid tying application objects to hard-coded resource lookups, Spring allows you to define resources like a JDBC DataSource or a Hibernate SessionFactory as beans in an application context. Application objects that need to access resources just receive references to such pre-defined instances via bean references (the DAO definition in the next section illustrates this). The following excerpt from an XML application context definition shows how to set up a JDBC DataSource and a Hibernate SessionFactory on top of it:
<beans> <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="org.hsqldb.jdbcDriver"/> <property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/> <property name="username" value="sa"/> <property name="password" value=""/> </bean> <bean id="mySessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"> <property name="dataSource" ref="myDataSource"/> <property name="mappingResources"> <list> <value>product.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop> </props> </property> </bean> ... </beans>
Note that switching from a local Jakarta Commons DBCP BasicDataSource to a JNDI-located DataSource (usually managed by the J2EE server) is just a matter of configuration:
<beans> <bean id="myDataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:comp/env/jdbc/myds"/> </bean> ... </beans>
You can also access a JNDI-located SessionFactory, using Spring's JndiObjectFactoryBean to retrieve and expose it. However, that's typically not necessary outside an EJB context. See the "container resources versus local resources" section for a discussion.
The basic programming model for templating looks as follows, for methods that can be part of any custom data access object or business service. There are no restrictions on the implementation of the surrounding object at all, it just needs to provide a Hibernate SessionFactory. It can get the latter from anywhere, but preferably as bean reference from a Spring application context - via a simple setSessionFactory bean property setter. The following snippets show a DAO definition in a Spring application context, referencing the above defined SessionFactory, and an example for a DAO method implementation.
<beans> ... <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="sessionFactory" ref="mySessionFactory"/> </bean> </beans>
public class ProductDaoImpl implements ProductDao { private SessionFactory sessionFactory; public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public Collection loadProductsByCategory(final String category) throws DataAccessException { HibernateTemplate ht = new HibernateTemplate(this.sessionFactory); return (Collection) ht.execute(new HibernateCallback() { public Object doInHibernate(Session session) throws HibernateException { Query query = session.createQuery( "from test.Product product where product.category=?"); query.setString(0, category); return query.list(); } }); } }
A callback implementation can effectively be used for any Hibernate data access. HibernateTemplate will ensure that Sessions are properly opened and closed, and automatically participate in transactions. The template instances are thread-safe and reusable, they can thus be kept as instance variables of the surrounding class. For simple single step actions like a single find, load, saveOrUpdate, or delete call, HibernateTemplate offers alternative convenience methods that can replace such one line callback implementations. Furthermore, Spring provides a convenient HibernateDaoSupport base class that provides a setSessionFactory method for receiving a SessionFactory, and getSessionFactory and getHibernateTemplate for use by subclasses. In combination, this allows for very simple DAO implementations for typical requirements:
public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao { public Collection loadProductsByCategory(String category) throws DataAccessException { return getHibernateTemplate().find( "from test.Product product where product.category=?", category); } }
As alternative to using Spring's HibernateTemplate to implement DAOs, data access code can also be written in a more traditional fashion, without wrapping the Hibernate access code in a callback, while still complying to Spring's generic DataAccessException hierarchy. Spring's HibernateDaoSupport base class offers methods to access the current transactional Session and to convert exceptions in such a scenario; similar methods are also available as static helpers on the SessionFactoryUtils class. Note that such code will usually pass "false" into getSession's the "allowCreate" flag, to enforce running within a transaction (which avoids the need to close the returned Session, as it its lifecycle is managed by the transaction).
public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao { public Collection loadProductsByCategory(String category) throws DataAccessException, MyException { Session session = getSession(getSessionFactory(), false); try { List result = session.find( "from test.Product product where product.category=?", category, Hibernate.STRING); if (result == null) { throw new MyException("invalid search result"); } return result; } catch (HibernateException ex) { throw convertHibernateAccessException(ex); } } }
The major advantage of such direct Hibernate access code is that it allows any checked application exception to be thrown within the data access code, while HibernateTemplate is restricted to unchecked exceptions within the callback. Note that one can often defer the corresponding checks and the throwing of application exceptions to after the callback, which still allows working with HibernateTemplate. In general, HibernateTemplate's convenience methods are simpler and more convenient for many scenarios.
Hibernate 3.0.1 introduced a feature called "contextual Sessions", where Hibernate itself manages one current Session per transaction. This is roughly equivalent to Spring's synchronization of one Hibernate Session per transaction. A corresponding DAO implementation looks like as follows, based on plain Hibernate API:
public class ProductDaoImpl implements ProductDao { private SessionFactory sessionFactory; public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public Collection loadProductsByCategory(String category) { return this.sessionFactory.getCurrentSession() .createQuery("from test.Product product where product.category=?") .setParameter(0, category) .list(); } }
This Hibernate access style is very similar to what you will find in the Hibernate documentation and examples, except for holding the SessionFactory in an instance variable. We strongly recommend such an instance-based setup over the old-school static HibernateUtil class from Hibernate's CaveatEmptor sample application! (In general, do not keep any resources in static variables unless absolutely necessary!)
Our DAO above follows the Dependency Injection pattern: It still fits nicely into a Spring application context, just like it would if coded against Spring's HibernateTemplate. Concretely, it uses Setter Injection; if desired, it could use Constructor Injection instead. Of course, such a DAO can also be set up in plain Java (for example, in unit tests): simply instantiate it and call setSessionFactory with the desired factory reference. As a Spring bean definition, it would look as follows:
<beans> ... <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="sessionFactory" ref="mySessionFactory"/> </bean> </beans>
The main advantage of this DAO style is that it depends on Hibernate API only; no import of any Spring class is required. This is of course appealing from a non-invasiveness perspective, and might feel more natural to Hibernate developers.
However, the DAO throws plain HibernateException (which is unchecked, so does not have to be declared or caught), which means that callers can only treat exceptions as generally fatal - unless they want to depend on Hibernate's own exception hierarchy. Catching specific causes such as an optimistic locking failure is not possible without tying the caller to the implementation strategy. This tradeoff might be acceptable to applications that are strongly Hibernate-based and/or do not need any special exception treatment.
A further disadvantage of that DAO style is that Hibernate's getCurrentSession() feature just works within JTA transactions. It does not work with any other transaction strategy out-of-the-box, in particular not with local Hibernate transactions.
Fortunately, Spring's LocalSessionFactoryBean supports Hibernate's SessionFactory.getCurrentSession() method for any Spring transaction strategy, returning the current Spring-managed transactional Session even with HibernateTransactionManager. Of course, the standard behavior of that method remains: returning the current Session associated with the ongoing JTA transaction, if any (no matter whether driven by Spring's JtaTransactionManager, by EJB CMT, or by plain JTA).
In summary: DAOs can be implemented based on plain Hibernate3 API, while still being able to participate in Spring-managed transactions. This might in particular appeal to people already familar with Hibernate, feeling more natural to them. However, such DAOs will throw plain HibernateException; conversion to Spring's DataAccessException would have to happen explicitly (if desired).
On top of such lower-level data access services, transactions can be demarcated in a higher level of the application, spanning any number of operations. There are no restrictions on the implementation of the surrounding business service here as well, it just needs a Spring PlatformTransactionManager. Again, the latter can come from anywhere, but preferably as bean reference via a setTransactionManager method - just like the productDAO should be set via a setProductDao method. The following snippets show a transaction manager and a business service definition in a Spring application context, and an example for a business method implementation.
<beans> ... <bean id="myTxManager" class="org.springframework.orm.hibernate.HibernateTransactionManager"> <property name="sessionFactory" ref="mySessionFactory"/> </bean> <bean id="myProductService" class="product.ProductServiceImpl"> <property name="transactionManager" ref="myTxManager"/> <property name="productDao" ref="myProductDao"/> </bean> </beans>
public class ProductServiceImpl implements ProductService { private PlatformTransactionManager transactionManager; private ProductDao productDao; public void setTransactionManager(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } public void setProductDao(ProductDao productDao) { this.productDao = productDao; } public void increasePriceOfAllProductsInCategory(final String category) { TransactionTemplate transactionTemplate = new TransactionTemplate(this.transactionManager); transactionTemplate.execute( new TransactionCallbackWithoutResult() { public void doInTransactionWithoutResult(TransactionStatus status) { List productsToChange = productDAO.loadProductsByCategory(category); ... } } ); } }
Alternatively, one can use Spring's AOP TransactionInterceptor, replacing the transaction demarcation code with an interceptor configuration in the application context. This allows you to keep business services free of repetitive transaction demarcation code in each business method. Furthermore, transaction semantics like propagation behavior and isolation level can be changed in a configuration file and do not affect the business service implementations.
<beans> ... <bean id="myTxManager" class="org.springframework.orm.hibernate.HibernateTransactionManager"> <property name="sessionFactory" ref="mySessionFactory"/> </bean> <bean id="myTxInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="myTxManager"/> <property name="transactionAttributeSource"> <value> product.ProductService.increasePrice*=PROPAGATION_REQUIRED product.ProductService.someOtherBusinessMethod=PROPAGATION_MANDATORY </value> </property> </bean> <bean id="myProductServiceTarget" class="product.ProductServiceImpl"> <property name="productDao" ref="myProductDao"/> </bean> <bean id="myProductService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>product.ProductService</value> </property> <property name="interceptorNames"> <list> <value>myTxInterceptor</value> <value>myProductServiceTarget</value> </list> </property> </bean> </beans>
public class ProductServiceImpl implements ProductService { private ProductDao productDao; public void setProductDao(ProductDao productDao) { this.productDao = productDao; } public void increasePriceOfAllProductsInCategory(final String category) { List productsToChange = this.productDAO.loadProductsByCategory(category); ... } ... }
Spring's TransactionInterceptor allows any checked application exception to be thrown with the callback code, while TransactionTemplate is restricted to unchecked exceptions within the callback. TransactionTemplate will trigger a rollback in case of an unchecked application exception, or if the transaction has been marked rollback-only by the application (via TransactionStatus). TransactionInterceptor behaves the same way by default but allows configurable rollback policies per method. A convenient alternative way of setting up declarative transactions is TransactionProxyFactoryBean, particularly if there are no other AOP interceptors involved. TransactionProxyFactoryBean combines the proxy definition itself with transaction configuration for a particular target bean. This reduces the configuration effort to one target bean plus one proxy bean. Furthermore, you do not need to specify which interfaces or classes the transactional methods are defined in.
<beans> ... <bean id="myTxManager" class="org.springframework.orm.hibernate.HibernateTransactionManager"> <property name="sessionFactory" ref="mySessionFactory"/> </bean> <bean id="myProductServiceTarget" class="product.ProductServiceImpl"> <property name="productDao" ref="myProductDao"/> </bean> <bean id="myProductService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="myTxManager"/> <property name="target" ref="myProductServiceTarget"/> <property name="transactionAttributes"> <props> <prop key="increasePrice*">PROPAGATION_REQUIRED</prop> <prop key="someOtherBusinessMethod">PROPAGATION_REQUIRES_NEW</prop> <prop key="*">PROPAGATION_SUPPORTS,readOnly</prop> </props> </property> </bean> </beans>
Both TransactionTemplate and TransactionInterceptor delegate the actual transaction handling to a PlatformTransactionManager instance, which can be a HibernateTransactionManager (for a single Hibernate SessionFactory, using a ThreadLocal Session under the hood) or a JtaTransactionManager (delegating to the JTA subsystem of the container) for Hibernate applications. You could even use a custom PlatformTransactionManager implementation. So switching from native Hibernate transaction management to JTA, i.e. when facing distributed transaction requirements for certain deployments of your application, is just a matter of configuration. Simply replace the Hibernate transaction manager with Spring's JTA transaction implementation. Both transaction demarcation and data access code will work without changes, as they just use the generic transaction management APIs.
For distributed transactions across multiple Hibernate session factories, simply combine JtaTransactionManager as a transaction strategy with multiple LocalSessionFactoryBean definitions. Each of your DAOs then gets one specific SessionFactory reference passed into its respective bean property. If all underlying JDBC data sources are transactional container ones, a business service can demarcate transactions across any number of DAOs and any number of session factories without special regard, as long as it is using JtaTransactionManager as the strategy.
<beans> <bean id="myDataSource1" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName value="java:comp/env/jdbc/myds1"/> </bean> <bean id="myDataSource2" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:comp/env/jdbc/myds2"/> </bean> <bean id="mySessionFactory1" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"> <property name="dataSource" ref="myDataSource1"/> <property name="mappingResources"> <list> <value>product.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop> </props> </property> </bean> <bean id="mySessionFactory2" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"> <property name="dataSource" ref="myDataSource2"/> <property name="mappingResources"> <list> <value>inventory.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">net.sf.hibernate.dialect.OracleDialect</prop> </props> </property> </bean> <bean id="myTxManager" class="org.springframework.transaction.jta.JtaTransactionManager"/> <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="sessionFactory" ref="mySessionFactory1"/> </bean> <bean id="myInventoryDao" class="product.InventoryDaoImpl"> <property name="sessionFactory" ref="mySessionFactory2"/> </bean> <bean id="myProductServiceTarget" class="product.ProductServiceImpl"> <property name="productDao" ref="myProductDao"/> <property name="inventoryDao" ref="myInventoryDao"/> </bean> <bean id="myProductService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="myTxManager"/> <property name="target" ref="myProductServiceTarget"/> <property name="transactionAttributes"> <props> <prop key="increasePrice*">PROPAGATION_REQUIRED</prop> <prop key="someOtherBusinessMethod">PROPAGATION_REQUIRES_NEW</prop> <prop key="*">PROPAGATION_SUPPORTS,readOnly</prop> </props> </property> </bean> </beans>
Both HibernateTransactionManager and JtaTransactionManager allow for proper JVM-level cache handling with Hibernate - without container-specific transaction manager lookup or JCA connector (as long as not using EJB to initiate transactions).
HibernateTransactionManager can export the JDBC Connection used by Hibernate to plain JDBC access code, for a specific DataSource. This allows for high-level transaction demarcation with mixed Hibernate/JDBC data access completely without JTA, as long as just accessing one database! HibernateTransactionManager will automatically expose the Hibernate transaction as JDBC transaction if the passed-in SessionFactory has been set up with a DataSource (through LocalSessionFactoryBean's "dataSource" property). Alternatively, the DataSource that the transactions are supposed to be exposed for can also be specified explicitly, through HibernateTransactionManager's "dataSource" property.
Note, for an alternative approach to using TransactionProxyFactoryBean to declaratively demarcate transactions, please see Section 8.5.2, “BeanNameAutoProxyCreator, another declarative approach”.
Spring's resource management allows for simple switching between a JNDI SessionFactory and a local one, same for a JNDI DataSource, without having to change a single line of application code. Whether to keep the resource definitions in the container or locally within the application, is mainly a matter of the transaction strategy being used. Compared to a Spring-defined local SessionFactory, a manually registered JNDI SessionFactory does not provide any benefits. Deploying a SessionFactory through Hibernate's JCA connector provides the added value of participating in the J2EE server's management infrastructure, but does not add actual value beyond that.
An important benefit of Spring's transaction support is that it isn't bound to a container at all. Configured to any other strategy than JTA, it will work in a standalone or test environment too. Especially for the typical case of single-database transactions, this is a very lightweight and powerful alternative to JTA. When using local EJB Stateless Session Beans to drive transactions, you depend both on an EJB container and JTA - even if you just access a single database anyway, and just use SLSBs for declarative transactions via CMT. The alternative of using JTA programmatically requires a J2EE environment as well. JTA does not just involve container dependencies in terms of JTA itself and of JNDI DataSources. For non-Spring JTA-driven Hibernate transactions, you have to use the Hibernate JCA connector, or extra Hibernate transaction code with the TransactionManagerLookup being configured - for proper JVM-level caching.
Spring-driven transactions can work with a locally defined Hibernate SessionFactory nicely, just like with a local JDBC DataSource - if accessing a single database, of course. Therefore you just have to fall back to Spring's JTA transaction strategy when actually facing distributed transaction requirements. Note that a JCA connector needs container-specific deployment steps, and obviously JCA support in the first place. This is far more hassle than deploying a simple web app with local resource definitions and Spring-driven transactions. And you often need the Enterprise Edition of your container, as e.g. WebLogic Express does not provide JCA. A Spring app with local resources and transactions spanning one single database will work in any J2EE web container (without JTA, JCA, or EJB) - like Tomcat, Resin, or even plain Jetty. Additionally, such a middle tier can be reused in desktop applications or test suites easily.
All things considered: If you do not use EJB, stick with local SessionFactory setup and Spring's HibernateTransactionManager or JtaTransactionManager. You will get all benefits including proper transactional JVM-level caching and distributed transactions, without any container deployment hassle. JNDI registration of a Hibernate SessionFactory via the JCA connector only adds value for use within EJBs.
In some JTA environments with very strict XADataSource implementations -- currently only some WebLogic and WebSphere versions -- when using Hibernate configured without any awareness of the JTA TransactionManager object for that environment, it is is possible for spurious warning or exceptions to show up in the application server log. These warnings or exceptions will say something to the effect that the connection being accessed is no longer valid, or JDBC access is no longer valid, possibly because the transaction is no longer active. As an example, here is an actual exception from WebLogic:
java.sql.SQLException: The transaction is no longer active - status: 'Committed'. No further JDBC access is allowed within this transaction.
This warning is easy to resolve by simply making Hibernate aware of the JTA TransactionManager instance, to which it will also synchronize (along with Spring). This may be done in two ways:
If in your application context you are already directly obtaining the JTA TransactionManager object (presumably from JNDI via JndiObjectFactoryBean) and feeding it for example to Spring's JtaTransactionManager, then the easiest way is to simply specify a reference to this as the value of LocalSessionFactoryBean's jtaTransactionManager property. Spring will then make the object available to Hibernate.
More likely you do not already have the JTA TransactionManager instance (since Spring's JtaTransactionManager can find it itself) so you need to instead configure Hibernate to also look it up directly. This is done by configuring an AppServer specific TransactionManagerLookup class in the Hibernate configuration, as described in the Hibernate manual.
It is not necessary to read any more for proper usage, bu the full sequence of events with and without Hibernate being aware of the JTA TransactionManager will now be described.
When Hibernate is not configured with any awareness of the JTA TransactionManager, the sequence of events when a JTA transaction commits is as follows:
JTA transaction commits
Spring's JtaTransactionManager is synchronized to the JTA transaction, so it is called back via an afterCompletion callback by the JTA transaction manager.
Among other activities, this can trigger a callback by Spring to Hibernate, via Hibernate's afterTransactionCompletion callback (used to clear the Hibernate cache), followed by an explicit close() call on the Hibernate Session, which results in Hibernate trying to close() the JDBC Connection.
In some environments, this Connection.close() call then triggers the warning or error, as the application server no longer considers the Connection usable at all, since the transaction has already been committed.
When Hibernate is configured with awareness of the JTA TransactionManager, the sequence of events when a JTA transaction commits is instead as follows:
JTA transaction is ready to commit
Spring's JtaTransactionManager is synchronized to the JTA transaction, so it is called back via a beforeCompletion callback by the JTA transaction manager.
Spring is aware that Hibernate itself is synchronized to the JTA Transaction, and behaves differently than in the previous senario. Assuming the Hibernate Session needs to be closed at all, Spring will close it now.
JTA Transaction commits
Hibernate is synchronized to the JTA transaction, so it is called back via an afterCompletion callback by the JTA transaction manager, and can properly clear its cache.
Spring supports the standard JDO 1.0/2.0 API as data access strategy, following the same style as the Hibernate support. The corresponding integration classes reside in the org.springframework.orm.jdo package.
Spring provides a LocalPersistenceManagerFactoryBean class that allows for defining a local JDO PersistenceManagerFactory within a Spring application context:
<beans> <bean id="myPmf" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean"> <property name="configLocation" value="classpath:kodo.properties"/> </bean> ... </beans>
Alternatively, a PersistenceManagerFactory can also be set up through direct instantiation of a PersistenceManagerFactory implementation class. A JDO PersistenceManagerFactory implementation class is supposed to follow the JavaBeans pattern, just like a JDBC DataSource implementation class, which is a natural fit for a Spring bean definition. This setup style usually supports a Spring-defined JDBC DataSource, passed into the "connectionFactory" property. For example, for the open source JDO implementation JPOX (http://www.jpox.org):
<beans> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="myPmf" class="org.jpox.PersistenceManagerFactoryImpl" destroy-method="close"> <property name="connectionFactory" ref="dataSource"/> <property name="nontransactionalRead" value="true"/> </bean> ... </beans>
A JDO PersistenceManagerFactory can also be set up in the JNDI environment of a J2EE application server, usually through the JCA connector provided by the particular JDO implementation. Spring's standard JndiObjectFactoryBean can be used to retrieve and expose such a PersistenceManagerFactory. However, outside an EJB context, there is often no compelling benefit in holding the PersistenceManagerFactory in JNDI: only choose such setup for a good reason. See "container resources versus local resources" in the Hibernate section for a discussion; the arguments there apply to JDO as well.
Each JDO-based DAO will then receive the PersistenceManagerFactory through dependency injection, i.e. through a bean property setter or through a constructor argument. Such a DAO could be coded against plain JDO API, working with the given PersistenceManagerFactory, but will usually rather be used with Spring's JdoTemplate:
<beans> ... <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="persistenceManagerFactory" ref="myPmf"/> </bean> </beans>
public class ProductDaoImpl implements ProductDao { private PersistenceManagerFactory persistenceManagerFactory; public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) { this.persistenceManagerFactory = pmf; } public Collection loadProductsByCategory(final String category) throws DataAccessException { JdoTemplate jdoTemplate = new JdoTemplate(this.persistenceManagerFactory); return (Collection) jdoTemplate.execute(new JdoCallback() { public Object doInJdo(PersistenceManager pm) throws JDOException { Query query = pm.newQuery(Product.class, "category = pCategory"); query.declareParameters("String pCategory"); List result = query.execute(category); // do some further stuff with the result list return result; } }); } }
A callback implementation can effectively be used for any JDO data access. JdoTemplate will ensure that PersistenceManagers are properly opened and closed, and automatically participate in transactions. The template instances are thread-safe and reusable, they can thus be kept as instance variables of the surrounding class. For simple single-step actions such as a single find, load, makePersistent, or delete call, JdoTemplate offers alternative convenience methods that can replace such one line callback implementations. Furthermore, Spring provides a convenient JdoDaoSupport base class that provides a setPersistenceManagerFactory method for receiving a PersistenceManagerFactory, and getPersistenceManagerFactory and getJdoTemplate for use by subclasses. In combination, this allows for very simple DAO implementations for typical requirements:
public class ProductDaoImpl extends JdoDaoSupport implements ProductDao { public Collection loadProductsByCategory(String category) throws DataAccessException { return getJdoTemplate().find( Product.class, "category = pCategory", "String category", new Object[] {category}); } }
As alternative to working with Spring's JdoTemplate, you can also code Spring-based DAOs at the JDO API level, explictly opening and closing a PersistenceManager. As elaborated in the corresponding Hibernate section, the main advantage of this approach is that your data access code is able to throw checked exceptions. JdoDaoSupport offers a variety of support methods for this scenario, for fetching and releasing a transactional PersistenceManager as well as for converting exceptions.
DAOs can also be written against plain JDO API, without any Spring dependencies, directly using an injected PersistenceManagerFactory. A corresponding DAO implementation looks like as follows:
public class ProductDaoImpl implements ProductDao { private PersistenceManagerFactory persistenceManagerFactory; public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) { this.persistenceManagerFactory = pmf; } public Collection loadProductsByCategory(String category) { PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager(); try { Query query = pm.newQuery(Product.class, "category = pCategory"); query.declareParameters("String pCategory"); return query.execute(category); } finally { pm.close(); } } }
As the above DAO still follows the Dependency Injection pattern, it still fits nicely into a Spring application context, just like it would if coded against Spring's JdoTemplate:
<beans> ... <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="persistenceManagerFactory" ref="myPmf"/> </bean> </beans>
The main issue with such DAOs is that they always get a new PersistenceManager from the factory. To still access a Spring-managed transactional PersistenceManager, consider defining a TransactionAwarePersistenceManagerFactoryProxy (as included in Spring) in front of your target PersistenceManagerFactory, passing the proxy into your DAOs.
<beans> ... <bean id="myPmfProxy" class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy"> <property name="targetPersistenceManagerFactory" ref="myPmf"/> </bean> <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="persistenceManagerFactory" ref="myPmfProxy"/> </bean> ... </beans>
Your data access code will then receive a transactional PersistenceManager (if any) from the PersistenceManagerFactory.getPersistenceManager() method that it calls. The latter method call goes through the proxy, which will first check for a current transactional PersistenceManager before getting a new one from the factory. close calls on the PersistenceManager will be ignored in case of a transaction PersistenceManager.
If your data access code will always run within an active transaction (or at least within active transaction synchronization), it is safe to omit the PersistenceManager.close() call and thus the entire finally block, which you might prefer to keep your DAO implementations concise:
public class ProductDaoImpl implements ProductDao { private PersistenceManagerFactory persistenceManagerFactory; public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) { this.persistenceManagerFactory = pmf; } public Collection loadProductsByCategory(String category) { PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager(); Query query = pm.newQuery(Product.class, "category = pCategory"); query.declareParameters("String pCategory"); return query.execute(category); } }
With such DAOs that rely on active transactions, it is recommended to enforce active transactions through turning TransactionAwarePersistenceManagerFactoryProxy's "allowCreate" flag off:
<beans> ... <bean id="myPmfProxy" class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy"> <property name="targetPersistenceManagerFactory" ref="myPmf"/> <property name="allowCreate" value="false"/> </bean> <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="persistenceManagerFactory" ref="myPmfProxy"/> </bean> ... </beans>
The main advantage of this DAO style is that it depends on JDO API only; no import of any Spring class is required. This is of course appealing from a non-invasiveness perspective, and might feel more natural to JDO developers.
However, the DAO throws plain JDOException (which is unchecked, so does not have to be declared or caught), which means that callers can only treat exceptions as generally fatal - unless they want to depend on JDO's own exception structure. Catching specific causes such as an optimistic locking failure is not possible without tying the caller to the implementation strategy. This tradeoff might be acceptable to applications that are strongly JDO-based and/or do not need any special exception treatment.
In summary: DAOs can be implemented based on plain JDO API, while still being able to participate in Spring-managed transactions. This might in particular appeal to people already familar with JDO, feeling more natural to them. However, such DAOs will throw plain JDOException; conversion to Spring's DataAccessException would have to happen explicitly (if desired).
To execute service operations within transactions, you can use Spring's common declarative transaction facilities. For example, you could define a TransactionProxyFactoryBean for a ProductService, which in turn delegates to the JDO-based ProductDao. Each specified method would then automatically get executed within a transaction, with all affected DAO operations automatically participating in it.
<beans> ... <bean id="myTxManager" class="org.springframework.orm.jdo.JdoTransactionManager"> <property name="persistenceManagerFactory" ref="myPmf"/> </bean> <bean id="myProductServiceTarget" class="product.ProductServiceImpl"> <property name="productDao" ref="myProductDao"/> </bean> <bean id="myProductService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="myTxManager"/> <property name="target" ref="myProductServiceTarget"/> <property name="transactionAttributes"> <props> <prop key="increasePrice*">PROPAGATION_REQUIRED</prop> <prop key="someOtherBusinessMethod">PROPAGATION_REQUIRES_NEW</prop> <prop key="*">PROPAGATION_SUPPORTS,readOnly</prop> </props> </property> </bean> </beans>
Note that JDO requires an active transaction when modifying a persistent object. There is no concept like a non-transactional flush in JDO, in contrast to Hibernate. For this reason, the chosen JDO implementation needs to be set up for a specific environment: in particular, it needs to be explicitly set up for JTA synchronization, to detect an active JTA transaction itself. This is not necessary for local transactions as performed by Spring's JdoTransactionManager, but it is necessary for participating in JTA transactions (whether driven by Spring's JtaTransactionManager or by EJB CMT / plain JTA).
JdoTransactionManager is capable of exposing a JDO transaction to JDBC access code that accesses the same JDBC DataSource, provided that the registered JdoDialect supports retrieval of the underlying JDBC Connection. This is by default the case for JDBC-based JDO 2.0 implementations; for JDO 1.0 implementations, a custom JdoDialect needs to be used. See next section for details on the JdoDialect mechanism.
As an advanced feature, both JdoTemplate and JdoTransactionManager support a custom JdoDialect, to be passed into the "jdoDialect" bean property. In such a scenario, the DAOs won't receive a PersistenceManagerFactory reference but rather a full JdoTemplate instance instead (for example, passed into JdoDaoSupport's "jdoTemplate" property). A JdoDialect implementation can enable some advanced 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)
applying query timeouts (automatically calculated from Spring-managed transaction timeout)
eagerly flushing a PersistenceManager (to make transactional changes visible to JDBC-based data access code)
advanced translation of JDOExceptions to Spring DataAccessExceptions
This is particularly valuable for JDO 1.0 implementations, where none of those features are covered by the standard API. On JDO 2.0, most of those features are supported in a standard manner: Hence, Spring's DefaultJdoDialect uses the corresponding JDO 2.0 API methods by default (as of Spring 1.2). For special transaction semantics and for advanced translation of exception, it is still valuable to derive vendor-specific JdoDialect subclasses.
See the JdoDialect javadoc for more details on its operations and how they are used within Spring's JDO support.
Since Spring 1.2, Spring supports Oracle TopLink (http://www.oracle.com/technology/products/ias/toplink) as data access strategy, following the same style as the Hibernate support. Both TopLink 9.0.4 (the production version as of Spring 1.2) and 10.1.3 (still in beta as of Spring 1.2) are supported. The corresponding integration classes reside in the org.springframework.orm.toplink package.
Spring's TopLink support has been co-developed with the Oracle TopLink team. Many thanks to the TopLink team, in particular to Jim Clark who helped to clarify details in all areas!
TopLink itself does not ship with a SessionFactory abstraction. Instead, multi-threaded access is based on the concept of a central ServerSession, which in turn is able to spawn ClientSessions for single-threaded usage. For flexible setup options, Spring defines a SessionFactory abstraction for TopLink, enabling to switch between different Session creation strategies.
As a one-stop shop, Spring provides a LocalSessionFactoryBean class that allows for defining a TopLink SessionFactory with bean-style configuration. It needs to be configured with the location of the TopLink session configuration file, and usually also receives a Spring-managed JDBC DataSource to use.
<beans> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="mySessionFactory" class="org.springframework.orm.toplink.LocalSessionFactoryBean"> <property name="configLocation" value="toplink-sessions.xml"/> <property name="dataSource" ref="dataSource"/> </bean> ... </beans>
<toplink-configuration> <session> <name>Session</name> <project-xml>toplink-mappings.xml</project-xml> <session-type> <server-session/> </session-type> <enable-logging>true</enable-logging> <logging-options/> </session> </toplink-configuration>
Usually, LocalSessionFactoryBean will hold a multi-threaded TopLink ServerSession underneath and create appropriate client Sessions for it: either a plain Session (typical), a managed ClientSession, or a transaction-aware Session (the latter are mainly used internally by Spring's TopLink support). It might also hold a single-threaded TopLink DatabaseSession; this is rather unusual, though.
Each TopLink-based DAO will then receive the SessionFactory through dependency injection, i.e. through a bean property setter or through a constructor argument. Such a DAO could be coded against plain TopLink API, fetching a Session from the given SessionFactory, but will usually rather be used with Spring's TopLinkTemplate:
<beans> ... <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="sessionFactory" ref="mySessionFactory"/> </bean> </beans>
public class ProductDaoImpl implements ProductDao { private SessionFactory sessionFactory; public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public Collection loadProductsByCategory(final String category) throws DataAccessException { TopLinkTemplate tlTemplate = new TopLinkTemplate(this.sessionFactory); return (Collection) tlTemplate.execute(new TopLinkCallback() { public Object doInTopLink(Session session) throws TopLinkException { ReadAllQuery findOwnersQuery = new ReadAllQuery(Product.class); findOwnersQuery.addArgument("Category"); ExpressionBuilder builder = this.findOwnersQuery.getExpressionBuilder(); findOwnersQuery.setSelectionCriteria( builder.get("category").like(builder.getParameter("Category"))); Vector args = new Vector(); args.add(category); List result = session.executeQuery(findOwnersQuery, args); // do some further stuff with the result list return result; } }); } }
A callback implementation can effectively be used for any TopLink data access. TopLinkTemplate will ensure that Sessions are properly opened and closed, and automatically participate in transactions. The template instances are thread-safe and reusable, they can thus be kept as instance variables of the surrounding class. For simple single-step actions such as a single executeQuery, readAll, readById, or merge call, JdoTemplate offers alternative convenience methods that can replace such one line callback implementations. Furthermore, Spring provides a convenient TopLinkDaoSupport base class that provides a setSessionFactory method for receiving a SessionFactory, and getSessionFactory and getTopLinkTemplate for use by subclasses. In combination, this allows for simple DAO implementations for typical requirements:
public class ProductDaoImpl extends TopLinkDaoSupport implements ProductDao { public Collection loadProductsByCategory(String category) throws DataAccessException { ReadAllQuery findOwnersQuery = new ReadAllQuery(Product.class); findOwnersQuery.addArgument("Category"); ExpressionBuilder builder = this.findOwnersQuery.getExpressionBuilder(); findOwnersQuery.setSelectionCriteria( builder.get("category").like(builder.getParameter("Category"))); return getTopLinkTemplate().executeQuery(findOwnersQuery, new Object[] {category}); } }
Side note: TopLink query objects are thread-safe and can be cached within the DAO, i.e. created on startup and kept in instance variables.
As alternative to working with Spring's TopLinkTemplate, you can also code your TopLink data access based on the raw TopLink API, explictly opening and closing a Session. As elaborated in the corresponding Hibernate section, the main advantage of this approach is that your data access code is able to throw checked exceptions. TopLinkDaoSupport offers a variety of support methods for this scenario, for fetching and releasing a transactional Session as well as for converting exceptions.
DAOs can also be written against plain TopLink API, without any Spring dependencies, directly using an injected TopLink Session. The latter will usually be based on a SessionFactory defined by a LocalSessionFactoryBean, exposed for bean references of type Session through Spring's TransactionAwareSessionAdapter.
The getActiveSession() method defined on TopLink's Session interface will return the current transactional Session in such a scenario. If there is no active transaction, it will return the shared TopLink ServerSession as-is, which is only supposed to be used directly for read-only access. There is also an analogous getActiveUnitOfWork() method, returning the TopLink UnitOfWork associated with the current transaction, if any (returning null else).
A corresponding DAO implementation looks like as follows:
public class ProductDaoImpl implements ProductDao { private Session session; public void setSession(Session session) { this.session = session; } public Collection loadProductsByCategory(String category) { ReadAllQuery findOwnersQuery = new ReadAllQuery(Product.class); findOwnersQuery.addArgument("Category"); ExpressionBuilder builder = this.findOwnersQuery.getExpressionBuilder(); findOwnersQuery.setSelectionCriteria( builder.get("category").like(builder.getParameter("Category"))); Vector args = new Vector(); args.add(category); return session.getActiveSession().executeQuery(findOwnersQuery, args); } }
As the above DAO still follows the Dependency Injection pattern, it still fits nicely into a Spring application context, analogous to like it would if coded against Spring's TopLinkTemplate. Spring's TransactionAwareSessionAdapter is used to expose a bean reference of type Session, to be passed into the DAO:
<beans> ... <bean id="mySessionAdapter" class="org.springframework.orm.toplink.support.TransactionAwareSessionAdapter"> <property name="sessionFactory" ref="mySessionFactory"/> </bean> <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="session" ref="mySessionAdapter"/> </bean> ... </beans>
The main advantage of this DAO style is that it depends on TopLink API only; no import of any Spring class is required. This is of course appealing from a non-invasiveness perspective, and might feel more natural to TopLink developers.
However, the DAO throws plain TopLinkException (which is unchecked, so does not have to be declared or caught), which means that callers can only treat exceptions as generally fatal - unless they want to depend on TopLink's own exception structure. Catching specific causes such as an optimistic locking failure is not possible without tying the caller to the implementation strategy. This tradeoff might be acceptable to applications that are strongly TopLink-based and/or do not need any special exception treatment.
A further disadvantage of that DAO style is that TopLink's standard getActiveSession() feature just works within JTA transactions. It does not work with any other transaction strategy out-of-the-box, in particular not with local TopLink transactions.
Fortunately, Spring's TransactionAwareSessionAdapter exposes a corresponding proxy for the TopLink ServerSession which supports TopLink's Session.getActiveSession() and Session.getActiveUnitOfWork() methods for any Spring transaction strategy, returning the current Spring-managed transactional Session even with TopLinkTransactionManager. Of course, the standard behavior of that method remains: returning the current Session associated with the ongoing JTA transaction, if any (no matter whether driven by Spring's JtaTransactionManager, by EJB CMT, or by plain JTA).
In summary: DAOs can be implemented based on plain TopLink API, while still being able to participate in Spring-managed transactions. This might in particular appeal to people already familar with TopLink, feeling more natural to them. However, such DAOs will throw plain TopLinkException; conversion to Spring's DataAccessException would have to happen explicitly (if desired).
To execute service operations within transactions, you can use Spring's common declarative transaction facilities. For example, you could define a TransactionProxyFactoryBean for a ProductService, which in turn delegates to the TopLink-based ProductDao. Each specified method would then automatically get executed within a transaction, with all affected DAO operations automatically participating in it.
<beans> ... <bean id="myTxManager" class="org.springframework.orm.toplink.TopLinkTransactionManager"> <property name="sessionFactory" ref="mySessionFactory"/> </bean> <bean id="myProductServiceTarget" class="product.ProductServiceImpl"> <property name="productDao" ref="myProductDao"/> </bean> <bean id="myProductService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="myTxManager"/> <property name="target" ref="myProductServiceTarget"/> <property name="transactionAttributes"> <props> <prop key="increasePrice*">PROPAGATION_REQUIRED</prop> <prop key="someOtherBusinessMethod">PROPAGATION_REQUIRES_NEW</prop> <prop key="*">PROPAGATION_SUPPORTS,readOnly</prop> </props> </property> </bean> </beans>
Note that TopLink requires an active UnitOfWork for modifying a persistent object. (You should never modify objects returned by a plain TopLink Session - those are usually read-only objects, directly taken from the second-level cache!) There is no concept like a non-transactional flush in TopLink, in contrast to Hibernate. For this reason, TopLink needs to be set up for a specific environment: in particular, it needs to be explicitly set up for JTA synchronization, to detect an active JTA transaction itself and expose a corresponding active Session and UnitOfWork. This is not necessary for local transactions as performed by Spring's TopLinkTransactionManager, but it is necessary for participating in JTA transactions (whether driven by Spring's JtaTransactionManager or by EJB CMT / plain JTA).
Within your TopLink-based DAO code, use the Session.getActiveUnitOfWork() method to access the current UnitOfWork and perform write operations through it. This will only work within an active transaction (both within Spring-managed transactions and plain JTA transactions). For special needs, you can also acquire separate UnitOfWork instances that won't participate in the current transaction; this is hardly needed, though.
TopLinkTransactionManager is capable of exposing a TopLink transaction to JDBC access code that accesses the same JDBC DataSource, provided that TopLink works with JDBC in the backend and is thus able to expose the underlying JDBC Connection. The DataSource to expose the transactions for needs to be specified explicitly; it won't be autodetected.
Apache OJB (http://db.apache.org/ojb) offers multiple API levels, such as ODMG and JDO. Aside from supporting OJB through JDO, Spring also supports OJB's lowe-level PersistenceBroker API as data access strategy. The corresponding integration classes reside in the org.springframework.orm.ojb package.
In contrast to Hibernate or JDO, OJB does not follow a factory object pattern for its resources. Instead, an OJB PersistenceBroker has to be obtained from the static PersistenceBrokerFactory class. That factory initializes itself from an OJB.properties file, residing in the root of the class path.
In addition to supporting OJB's default initialization style, Spring also provides a LocalOjbConfigurer class that allows for using Spring-managed DataSource instances as OJB connection providers. The DataSource instances are referenced in the OJB repository descriptor (the mapping file), through the "jcd-alias" defined there: each such alias is matched against the Spring-managed bean of the same name.
<beans> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="ojbConfigurer" class="org.springframework.orm.ojb.support.LocalOjbConfigurer"/> ... </beans>
<descriptor-repository version="1.0"> <jdbc-connection-descriptor jcd-alias="dataSource" default-connection="true" ...> ... </jdbc-connection-descriptor> ... </descriptor-repository>
A PersistenceBroker can then be opened through standard OJB API, specifying a corresponding "PBKey", usually through the corresponding "jcd-alias" (or relying on the default connection).
Each OJB-based DAO will be configured with a "PBKey" through bean-style configuration, i.e. through a bean property setter. Such a DAO could be coded against plain OJB API, working with OJB's static PersistenceBrokerFactory, but will usually rather be used with Spring's PersistenceBrokerTemplate:
<beans> ... <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="jcdAlias" value="dataSource"/> <!-- can be omitted (default) --> </bean> </beans>
public class ProductDaoImpl implements ProductDao { private String jcdAlias; public void setJcdAlias(String jcdAlias) { this.jcdAlias = jcdAlias; } public Collection loadProductsByCategory(final String category) throws DataAccessException { PersistenceBrokerTemplate pbTemplate = new PersistenceBrokerTemplate(new PBKey(this.jcdAlias); return (Collection) pbTemplate.execute(new PersistenceBrokerCallback() { public Object doInPersistenceBroker(PersistenceBroker pb) throws PersistenceBrokerException { Criteria criteria = new Criteria(); criteria.addLike("category", category + "%"); Query query = new QueryByCriteria(Product.class, criteria); List result = pb.getCollectionByQuery(query); // do some further stuff with the result list return result; } }); } }
A callback implementation can effectively be used for any OJB data access. PersistenceBrokerTemplate will ensure that PersistenceBrokers are properly opened and closed, and automatically participate in transactions. The template instances are thread-safe and reusable, they can thus be kept as instance variables of the surrounding class. For simple single-step actions such as a single getObjectById, getObjectByQuery, store, or delete call, PersistenceBrokerTemplate offers alternative convenience methods that can replace such one line callback implementations. Furthermore, Spring provides a convenient PersistenceBrokerDaoSupport base class that provides a setJcdAlias method for receiving an OJB JCD alias, and getPersistenceBrokerTemplate for use by subclasses. In combination, this allows for very simple DAO implementations for typical requirements:
public class ProductDaoImpl extends PersistenceBrokerDaoSupport implements ProductDao { public Collection loadProductsByCategory(String category) throws DataAccessException { Criteria criteria = new Criteria(); criteria.addLike("category", category + "%"); Query query = new QueryByCriteria(Product.class, criteria); return getPersistenceBrokerTemplate().getCollectionByQuery(query); } }
As alternative to working with Spring's PersistenceBrokerTemplate, you can also code your OJB data access against plain OJB API, explictly opening and closing a PersistenceBroker. As elaborated in the corresponding Hibernate section, the main advantage of this approach is that your data access code is able to throw checked exceptions. PersistenceBrokerDaoSupport offers a variety of support methods for this scenario, for fetching and releasing a transactional PersistenceBroker as well as for converting exceptions.
To execute service operations within transactions, you can use Spring's common declarative transaction facilities. For example, you could define a TransactionProxyFactoryBean for a ProductService, which in turn delegates to the OJB-based ProductDao. Each specified method would then automatically get executed within a transaction, with all affected DAO operations automatically participating in it.
<beans> ... <bean id="myTxManager" class="org.springframework.orm.ojb.PersistenceBrokerTransactionManager"> <property name="jcdAlias" value="dataSource"/> <!-- can be omitted (default) --> </bean> <bean id="myProductServiceTarget" class="product.ProductServiceImpl"> <property name="productDao" ref="myProductDao"/> </bean> <bean id="myProductService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="myTxManager"/> <property name="target" ref="myProductServiceTarget"/> <property name="transactionAttributes"> <props> <prop key="increasePrice*">PROPAGATION_REQUIRED</prop> <prop key="someOtherBusinessMethod">PROPAGATION_REQUIRES_NEW</prop> <prop key="*">PROPAGATION_SUPPORTS,readOnly</prop> </props> </property> </bean> </beans>
Note that OJB's PersistenceBroker level does not track changes of loaded objects. Therefore, a PersistenceBroker transaction is essentially simply a database transaction at the PersistenceBroker level, just with an additional first-level cache for persistent objects. Lazy loading will work both with and without the PersistenceBroker being open, in contrast to Hibernate and JDO (where the original Session or PersistenceManager, respectively, needs to remain open).
PersistenceBrokerTransactionManager is capable of exposing an OJB transaction to JDBC access code that accesses the same JDBC DataSource. The DataSource to expose the transactions for needs to be specified explicitly; it won't be autodetected.
Through the org.springframework.orm.ibatis package, Spring supports iBATIS SQL Maps 1.x and 2.x (http://www.ibatis.com). The iBATIS support much resembles the JDBC / Hibernate support in that it supports the same template style programming and just as with JDBC or Hibernate, the iBATIS support works with Spring's exception hierarchy and let's you enjoy the all IoC features Spring has.
Transaction management can be handled through Spring's standard facilities, for example through TransactionProxyFactoryBean. There are no special transaction strategies for iBATIS, as there is no special transactional resource involved other than a JDBC Connection. Hence, Spring's standard JDBC DataSourceTransactionManager or JtaTransactionManager are perfectly sufficient.
Spring supports both iBATIS SQL Maps 1.x and 2.x. First let's have a look at the differences between the two.
The XML config files have changed a bit, node and attribute names. Also the Spring classes you need to extend are different, as are some method names.
Table 12.1. iBATIS SQL Maps supporting classes for 1.x and 2.x
Feature | 1.x | 2.x |
---|---|---|
Creation of SqlMap(Client) | SqlMapFactoryBean | SqlMapClientFactoryBean |
Template-style helper class | SqlMapTemplate | SqlMapClientTemplate |
Callback to use MappedStatement | SqlMapCallback | SqlMapClientCallback |
Super class for DAOs | SqlMapDaoSupport | SqlMapClientDaoSupport |
Using iBATIS SQL Maps involves creating SqlMap configuration files containing statements and result maps. Spring takes care of loading those using the SqlMapFactoryBean.
public class Account { private String name; private String email; public String getName() { return this.name; } public void setName(String name) { this.name = name; } public String getEmail() { return this.email; } public void setEmail(String email) { this.email = email; } }
Suppose we would want to map this class. We'd have to create the following SqlMap. Using the query, we can later on retrieve users through their email addresses. Account.xml:
<sql-map name="Account"> <result-map name="result" class="examples.Account"> <property name="name" column="NAME" columnIndex="1"/> <property name="email" column="EMAIL" columnIndex="2"/> </result-map> <mapped-statement name="getAccountByEmail" result-map="result"> select ACCOUNT.NAME, ACCOUNT.EMAIL from ACCOUNT where ACCOUNT.EMAIL = #value# </mapped-statement> <mapped-statement name="insertAccount"> insert into ACCOUNT (NAME, EMAIL) values (#name#, #email#) </mapped-statement> </sql-map>
After having defined the Sql Map, we have to create a configuration file for iBATIS (sqlmap-config.xml):
<sql-map-config> <sql-map resource="example/Account.xml"/> </sql-map-config>
iBATIS loads resources from the class path, so be sure to add the Account.xml file to the class path.
Using Spring, we can now very easily set up the SqlMap, using the SqlMapFactoryBean:
<beans> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="sqlMap" class="org.springframework.orm.ibatis.SqlMapFactoryBean"> <property name="configLocation" value="WEB-INF/sqlmap-config.xml"/> </bean> ... </beans>
The SqlMapDaoSupport class offers a supporting class similar to the HibernateDaoSupport and the JdoDaoSupport classes. Let's implement a DAO:
public class SqlMapAccountDao extends SqlMapDaoSupport implements AccountDao { public Account getAccount(String email) throws DataAccessException { return (Account) getSqlMapTemplate().executeQueryForObject("getAccountByEmail", email); } public void insertAccount(Account account) throws DataAccessException { getSqlMapTemplate().executeUpdate("insertAccount", account); } }
As you can see, we're using the pre-configured SqlMapTemplate to execute the query. Spring has initialized the SqlMap for us using the SqlMapFactoryBean, and when setting up the SqlMapAccountDao as follows, you're all set to go. Note that with iBATIS SQL Maps 1.x, the JDBC DataSource is usually specified on the DAO.
<beans> ... <bean id="accountDao" class="example.SqlMapAccountDao"> <property name="dataSource" ref="dataSource"/> <property name="sqlMap" ref="sqlMap"/> </bean> </beans>
Note that a SqlMapTemplate instance could also be created manually, passing in the DataSource and the SqlMap as constructor arguments. The SqlMapDaoSupport base class simply pre-initializes a SqlMapTemplate instance for us.
If we want to map the previous Account class with iBATIS 2.x we need to create the following SQL map Account.xml:
<sqlMap namespace="Account"> <resultMap id="result" class="examples.Account"> <result property="name" column="NAME" columnIndex="1"/> <result property="email" column="EMAIL" columnIndex="2"/> </resultMap> <select id="getAccountByEmail" resultMap="result"> select ACCOUNT.NAME, ACCOUNT.EMAIL from ACCOUNT where ACCOUNT.EMAIL = #value# </select> <insert id="insertAccount"> insert into ACCOUNT (NAME, EMAIL) values (#name#, #email#) </insert> </sqlMap>
The configuration file for iBATIS 2 changes a bit (sqlmap-config.xml):
<sqlMapConfig> <sqlMap resource="example/Account.xml"/> </sqlMapConfig>
Remember that iBATIS loads resources from the class path, so be sure to add the Account.xml file to the class path.
We can use the SqlMapClientFactoryBean in the Spring application context. Note that with iBATIS SQL Maps 2.x, the JDBC DataSource is usually specified on the SqlMapClientFactoryBean, which enables lazy loading.
<beans> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation" value="WEB-INF/sqlmap-config.xml"/> <property name="dataSource" ref="dataSource"/> </bean> ... </beans>
The SqlMapClientDaoSupport class offers a supporting class similar to the SqlMapDaoSupport. We extend it to implement our DAO:
public class SqlMapAccountDao extends SqlMapClientDaoSupport implements AccountDao { public Account getAccount(String email) throws DataAccessException { return (Account) getSqlMapClientTemplate().queryForObject("getAccountByEmail", email); } public void insertAccount(Account account) throws DataAccessException { getSqlMapClientTemplate().update("insertAccount", account); } }
In the DAO, we use the pre-configured SqlMapClientTemplate to execute the queries, after setting up the SqlMapAccountDao in the application context and wiring it with our SqlMapClient instance:
<beans> ... <bean id="accountDao" class="example.SqlMapAccountDao"> <property name="sqlMapClient" ref="sqlMapClient"/> </bean> </beans>
Note that a SqlMapTemplate instance could also be created manually, passing in the SqlMapClient as constructor argument. The SqlMapClientDaoSupport base class simply pre-initializes a SqlMapClientTemplate instance for us.
The SqlMapClientTemplate also offers a generic execute method, taking a custom SqlMapClientCallback implementation as argument. This can, for example, be used for batching:
public class SqlMapAccountDao extends SqlMapClientDaoSupport implements AccountDao { ... public void insertAccount(Account account) throws DataAccessException { getSqlMapClientTemplate().execute(new SqlMapClientCallback() { public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException { executor.startBatch(); executor.update("insertAccount", account); executor.update("insertAddress", account.getAddress()); executor.executeBatch(); } }); } }
In general, any combination of operations offered by the native SqlMapExecutor API can be used in such a callback. Any SQLException thrown will automatically get converted to Spring's generic DataAccessException hierarchy.
DAOs can also be written against plain iBATIS API, without any Spring dependencies, directly using an injected SqlMapClient. A corresponding DAO implementation looks like as follows:
public class SqlMapAccountDao implements AccountDao { private SqlMapClient sqlMapClient; public void setSqlMapClient(SqlMapClient sqlMapClient) { this.sqlMapClient = sqlMapClient; } public Account getAccount(String email) { try { return (Account) this.sqlMapClient.queryForObject("getAccountByEmail", email); } catch (SQLException ex) { throw new MyDaoException(ex); } } public void insertAccount(Account account) throws DataAccessException { try { this.sqlMapClient.update("insertAccount", account); } catch (SQLException ex) { throw new MyDaoException(ex); } } }
In such a scenario, the SQLException thrown by the iBATIS API needs to be handled in a custom fashion: usually, wrapping it in your own application-specific DAO exception. Wiring in the application context would still look like before, due to the fact that the plain iBATIS-based DAO still follows the Dependency Injection pattern:
<beans> ... <bean id="accountDao" class="example.SqlMapAccountDao"> <property name="sqlMapClient" ref="sqlMapClient"/> </bean> </beans>