2725 lines
130 KiB
XML
2725 lines
130 KiB
XML
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
||
|
|
<chapter id="orm">
|
||
|
|
<title>Object Relational Mapping (ORM) data access</title>
|
||
|
|
|
||
|
|
<section id="orm-introduction">
|
||
|
|
<title>Introduction</title>
|
||
|
|
|
||
|
|
<para>The Spring Framework provides integration with <emphasis>Hibernate,
|
||
|
|
JDO, Oracle TopLink, iBATIS SQL Maps</emphasis> and
|
||
|
|
<emphasis>JPA</emphasis>: 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 (Object Relational) 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.</para>
|
||
|
|
|
||
|
|
<para>Spring 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 a Spring IoC container does provide additional benefits in
|
||
|
|
terms of ease of configuration and deployment; as such, most examples in
|
||
|
|
this section show configuration inside a Spring container.</para>
|
||
|
|
|
||
|
|
<para>Some of the benefits of using the Spring Framework to create your
|
||
|
|
ORM DAOs include:</para>
|
||
|
|
|
||
|
|
<itemizedlist>
|
||
|
|
<listitem>
|
||
|
|
<para><emphasis>Ease of testing.</emphasis> Spring's IoC approach
|
||
|
|
makes it easy to swap the implementations and config locations of
|
||
|
|
Hibernate <interfacename>SessionFactory</interfacename> instances,
|
||
|
|
JDBC <interfacename>DataSource</interfacename> instances, transaction
|
||
|
|
managers, and mappes object implementations (if needed). This makes it
|
||
|
|
much easier to isolate and test each piece of persistence-related code
|
||
|
|
in isolation.</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para><emphasis>Common data access exceptions.</emphasis> Spring can
|
||
|
|
wrap exceptions from your 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.</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para><emphasis>General resource management.</emphasis> Spring
|
||
|
|
application contexts can handle the location and configuration of
|
||
|
|
Hibernate <interfacename>SessionFactory</interfacename> instances,
|
||
|
|
JDBC <interfacename>DataSource</interfacename> instances, 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
|
||
|
|
<interfacename>Session</interfacename> for efficiency and proper
|
||
|
|
transaction handling. Spring makes it easy to transparently create and
|
||
|
|
bind a <interfacename>Session</interfacename> to the current thread,
|
||
|
|
either by using an explicit 'template' wrapper class at the Java code
|
||
|
|
level or by exposing a current <interfacename>Session</interfacename>
|
||
|
|
through the Hibernate <interfacename>SessionFactory</interfacename>
|
||
|
|
(for DAOs based on plain Hibernate API). Thus Spring solves many of
|
||
|
|
the issues that repeatedly arise from typical Hibernate usage, for any
|
||
|
|
transaction environment (local or JTA).</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para><emphasis>Integrated transaction management.</emphasis> 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 ORM operations.</para>
|
||
|
|
</listitem>
|
||
|
|
</itemizedlist>
|
||
|
|
|
||
|
|
<para>The PetClinic sample in the Spring distribution offers alternative
|
||
|
|
DAO implementations and application context configurations for JDBC,
|
||
|
|
Hibernate, Oracle TopLink, and JPA. PetClinic can therefore serve as
|
||
|
|
working sample app that illustrates the use of Hibernate, TopLink and JPA
|
||
|
|
in a Spring web application. It also leverages declarative transaction
|
||
|
|
demarcation with different transaction strategies.</para>
|
||
|
|
|
||
|
|
<para>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.</para>
|
||
|
|
|
||
|
|
<para>Beyond the samples shipped with Spring, there are a variety of
|
||
|
|
Spring-based O/R mapping samples provided by specific vendors: for
|
||
|
|
example, the JDO implementations JPOX (<ulink
|
||
|
|
url="http://www.jpox.org/"></ulink>) and Kodo (<ulink
|
||
|
|
url="http://www.bea.com/kodo/"></ulink>).</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-hibernate">
|
||
|
|
<title>Hibernate</title>
|
||
|
|
|
||
|
|
<para>We will start with a coverage of <ulink
|
||
|
|
url="http://www.hibernate.org/">Hibernate 3</ulink> 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 demarcation. Most of these patterns can be directly translated
|
||
|
|
to all other supported ORM tools. The following sections in this chapter
|
||
|
|
will then cover the other ORM technologies, showing briefer examples
|
||
|
|
there.</para>
|
||
|
|
|
||
|
|
<para><emphasis>Note: As of Spring 2.5, Spring requires Hibernate 3.1 or
|
||
|
|
higher. Neither Hibernate 2.1 nor Hibernate 3.0 are supported
|
||
|
|
anymore.</emphasis></para>
|
||
|
|
|
||
|
|
<section id="orm-resource-mngmnt">
|
||
|
|
<title>Resource management</title>
|
||
|
|
|
||
|
|
<para>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, namely IoC via templating; for
|
||
|
|
example 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
|
||
|
|
<classname>JdbcTemplate</classname> class mentioned in a previous
|
||
|
|
section cares for connection handling, and for proper conversion of
|
||
|
|
<classname>SQLException</classname> to the
|
||
|
|
<classname>DataAccessException</classname> 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.</para>
|
||
|
|
|
||
|
|
<para>Spring also offers Hibernate and JDO support, consisting of a
|
||
|
|
<classname>HibernateTemplate</classname> /
|
||
|
|
<classname>JdoTemplate</classname> analogous to
|
||
|
|
<classname>JdbcTemplate</classname>, a
|
||
|
|
<classname>HibernateInterceptor</classname> /
|
||
|
|
<classname>JdoInterceptor</classname>, 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
|
||
|
|
application, 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),and so on.</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-session-factory-setup">
|
||
|
|
<title><interfacename>SessionFactory</interfacename> setup in a Spring
|
||
|
|
container</title>
|
||
|
|
|
||
|
|
<para>To avoid tying application objects to hard-coded resource lookups,
|
||
|
|
Spring allows you to define resources such as a JDBC
|
||
|
|
<interfacename>DataSource</interfacename> or a Hibernate
|
||
|
|
<interfacename>SessionFactory</interfacename> as beans in the Spring
|
||
|
|
container. 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 <classname>DataSource</classname> and a Hibernate
|
||
|
|
<interfacename>SessionFactory</interfacename> on top of it:</para>
|
||
|
|
|
||
|
|
<programlisting><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.hibernate3.LocalSessionFactoryBean">
|
||
|
|
<property name="dataSource" ref="myDataSource"/>
|
||
|
|
<property name="mappingResources">
|
||
|
|
<list>
|
||
|
|
<value>product.hbm.xml</value>
|
||
|
|
</list>
|
||
|
|
</property>
|
||
|
|
<property name="hibernateProperties">
|
||
|
|
<value>
|
||
|
|
hibernate.dialect=org.hibernate.dialect.HSQLDialect
|
||
|
|
</value>
|
||
|
|
</property>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<para>Note that switching from a local Jakarta Commons DBCP
|
||
|
|
<classname>BasicDataSource</classname> to a JNDI-located
|
||
|
|
<interfacename>DataSource</interfacename> (usually managed by an
|
||
|
|
application server) is just a matter of configuration:</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<bean id="myDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
|
||
|
|
<property name="jndiName" value="java:comp/env/jdbc/myds"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<para>You can also access a JNDI-located
|
||
|
|
<interfacename>SessionFactory</interfacename>, using Spring's
|
||
|
|
<classname>JndiObjectFactoryBean</classname> to retrieve and expose it.
|
||
|
|
However, that is typically not common outside of an EJB context.</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-hibernate-template">
|
||
|
|
<title>The <classname>HibernateTemplate</classname></title>
|
||
|
|
|
||
|
|
<para>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
|
||
|
|
<interfacename>SessionFactory</interfacename>. It can get the latter
|
||
|
|
from anywhere, but preferably as bean reference from a Spring IoC
|
||
|
|
container - via a simple <methodname>setSessionFactory(..)</methodname>
|
||
|
|
bean property setter. The following snippets show a DAO definition in a
|
||
|
|
Spring container, referencing the above defined
|
||
|
|
<interfacename>SessionFactory</interfacename>, and an example for a DAO
|
||
|
|
method implementation.</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<bean id="myProductDao" class="product.ProductDaoImpl">
|
||
|
|
<property name="sessionFactory" ref="mySessionFactory"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<programlisting>public class ProductDaoImpl implements ProductDao {
|
||
|
|
|
||
|
|
private HibernateTemplate hibernateTemplate;
|
||
|
|
|
||
|
|
public void setSessionFactory(SessionFactory sessionFactory) {
|
||
|
|
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
|
||
|
|
}
|
||
|
|
|
||
|
|
public Collection loadProductsByCategory(String category) throws DataAccessException {
|
||
|
|
return this.hibernateTemplate.find("from test.Product product where product.category=?", category);
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>The <classname>HibernateTemplate</classname> class provides many
|
||
|
|
methods that mirror the methods exposed on the Hibernate
|
||
|
|
<interfacename>Session</interfacename> interface, in addition to a
|
||
|
|
number of convenience methods such as the one shown above. If you need
|
||
|
|
access to the <interfacename>Session</interfacename> to invoke methods
|
||
|
|
that are not exposed on the <classname>HibernateTemplate</classname>,
|
||
|
|
you can always drop down to a callback-based approach like so.</para>
|
||
|
|
|
||
|
|
<programlisting>public class ProductDaoImpl implements ProductDao {
|
||
|
|
|
||
|
|
private HibernateTemplate hibernateTemplate;
|
||
|
|
|
||
|
|
public void setSessionFactory(SessionFactory sessionFactory) {
|
||
|
|
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
|
||
|
|
}
|
||
|
|
|
||
|
|
public Collection loadProductsByCategory(final String category) throws DataAccessException {
|
||
|
|
return this.hibernateTemplate.execute(new HibernateCallback() {
|
||
|
|
|
||
|
|
public Object doInHibernate(Session session) {
|
||
|
|
Criteria criteria = session.createCriteria(Product.class);
|
||
|
|
criteria.add(Expression.eq("category", category));
|
||
|
|
criteria.setMaxResults(6);
|
||
|
|
return criteria.list();
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>A callback implementation effectively can be used for any
|
||
|
|
Hibernate data access. <classname>HibernateTemplate</classname> will
|
||
|
|
ensure that <interfacename>Session</interfacename> instances 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, <classname>HibernateTemplate</classname> offers alternative
|
||
|
|
convenience methods that can replace such one line callback
|
||
|
|
implementations. Furthermore, Spring provides a convenient
|
||
|
|
<classname>HibernateDaoSupport</classname> base class that provides a
|
||
|
|
<methodname>setSessionFactory(..)</methodname> method for receiving a
|
||
|
|
<interfacename>SessionFactory</interfacename>, and
|
||
|
|
<methodname>getSessionFactory()</methodname> and
|
||
|
|
<methodname>getHibernateTemplate()</methodname>for use by subclasses. In
|
||
|
|
combination, this allows for very simple DAO implementations for typical
|
||
|
|
requirements:</para>
|
||
|
|
|
||
|
|
<programlisting>public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {
|
||
|
|
|
||
|
|
public Collection loadProductsByCategory(String category) throws DataAccessException {
|
||
|
|
return this.getHibernateTemplate().find(
|
||
|
|
"from test.Product product where product.category=?", category);
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-hibernate-daos">
|
||
|
|
<title>Implementing Spring-based DAOs without callbacks</title>
|
||
|
|
|
||
|
|
<para>As alternative to using Spring's
|
||
|
|
<classname>HibernateTemplate</classname> 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 respecting and
|
||
|
|
participating in Spring's generic
|
||
|
|
<classname>DataAccessException</classname> hierarchy. The
|
||
|
|
<classname>HibernateDaoSupport</classname> base class offers methods to
|
||
|
|
access the current transactional <interfacename>Session</interfacename>
|
||
|
|
and to convert exceptions in such a scenario; similar methods are also
|
||
|
|
available as static helpers on the
|
||
|
|
<classname>SessionFactoryUtils</classname> class. Note that such code
|
||
|
|
will usually pass '<literal>false</literal>' as the value of the
|
||
|
|
<methodname>getSession(..)</methodname> methods
|
||
|
|
'<literal>allowCreate</literal>' argument, to enforce running within a
|
||
|
|
transaction (which avoids the need to close the returned
|
||
|
|
<interfacename>Session</interfacename>, as its lifecycle is managed by
|
||
|
|
the transaction).</para>
|
||
|
|
|
||
|
|
<programlisting>public class HibernateProductDao extends HibernateDaoSupport implements ProductDao {
|
||
|
|
|
||
|
|
public Collection loadProductsByCategory(String category) throws DataAccessException, MyException {
|
||
|
|
Session session = getSession(false);
|
||
|
|
try {
|
||
|
|
Query query = session.createQuery("from test.Product product where product.category=?");
|
||
|
|
query.setString(0, category);
|
||
|
|
List result = query.list();
|
||
|
|
if (result == null) {
|
||
|
|
throw new MyException("No search results.");
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
catch (HibernateException ex) {
|
||
|
|
throw convertHibernateAccessException(ex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>The advantage of such direct Hibernate access code is that it
|
||
|
|
allows <emphasis>any</emphasis> checked application exception to be
|
||
|
|
thrown within the data access code; contrast this to the
|
||
|
|
<classname>HibernateTemplate</classname> class which is restricted to
|
||
|
|
throwing only unchecked exceptions within the callback. Note that you
|
||
|
|
can often defer the corresponding checks and the throwing of application
|
||
|
|
exceptions to after the callback, which still allows working with
|
||
|
|
<classname>HibernateTemplate</classname>. In general, the
|
||
|
|
<classname>HibernateTemplate</classname> class' convenience methods are
|
||
|
|
simpler and more convenient for many scenarios.</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-hibernate-straight">
|
||
|
|
<title>Implementing DAOs based on plain Hibernate 3 API</title>
|
||
|
|
|
||
|
|
<para>Hibernate 3 provides a feature called "contextual Sessions", where
|
||
|
|
Hibernate itself manages one current
|
||
|
|
<interfacename>Session</interfacename> per transaction. This is roughly
|
||
|
|
equivalent to Spring's synchronization of one Hibernate
|
||
|
|
<interfacename>Session</interfacename> per transaction. A corresponding
|
||
|
|
DAO implementation looks like as follows, based on the plain Hibernate
|
||
|
|
API:</para>
|
||
|
|
|
||
|
|
<programlisting>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();
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>This style is very similar to what you will find in the Hibernate
|
||
|
|
reference documentation and examples, except for holding the
|
||
|
|
<interfacename>SessionFactory</interfacename> in an instance variable.
|
||
|
|
We strongly recommend such an instance-based setup over the old-school
|
||
|
|
<literal>static</literal> <classname>HibernateUtil</classname> class
|
||
|
|
from Hibernate's CaveatEmptor sample application. (In general, do not
|
||
|
|
keep any resources in <literal>static</literal> variables unless
|
||
|
|
<emphasis>absolutely</emphasis> necessary.)</para>
|
||
|
|
|
||
|
|
<para>The above DAO follows the Dependency Injection pattern: it fits
|
||
|
|
nicely into a Spring IoC container, just like it would if coded against
|
||
|
|
Spring's <classname>HibernateTemplate</classname>. Of course, such a DAO
|
||
|
|
can also be set up in plain Java (for example, in unit tests): simply
|
||
|
|
instantiate it and call <methodname>setSessionFactory(..)</methodname>
|
||
|
|
with the desired factory reference. As a Spring bean definition, it
|
||
|
|
would look as follows:</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<bean id="myProductDao" class="product.ProductDaoImpl">
|
||
|
|
<property name="sessionFactory" ref="mySessionFactory"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<para>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 will no
|
||
|
|
doubt feel more natural to Hibernate developers.</para>
|
||
|
|
|
||
|
|
<para>However, the DAO throws plain
|
||
|
|
<classname>HibernateException</classname> (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 tieing 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.</para>
|
||
|
|
|
||
|
|
<para>Fortunately, Spring's
|
||
|
|
<classname>LocalSessionFactoryBean</classname> supports Hibernate's
|
||
|
|
<methodname>SessionFactory.getCurrentSession()</methodname> method for
|
||
|
|
any Spring transaction strategy, returning the current Spring-managed
|
||
|
|
transactional <interfacename>Session</interfacename> even with
|
||
|
|
<classname>HibernateTransactionManager</classname>. Of course, the
|
||
|
|
standard behavior of that method remains: returning the current
|
||
|
|
<interfacename>Session</interfacename> associated with the ongoing JTA
|
||
|
|
transaction, if any (no matter whether driven by Spring's
|
||
|
|
<classname>JtaTransactionManager</classname>, by EJB CMT, or by
|
||
|
|
JTA).</para>
|
||
|
|
|
||
|
|
<para>In summary: DAOs can be implemented based on the plain Hibernate 3
|
||
|
|
API, while still being able to participate in Spring-managed
|
||
|
|
transactions.</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-hibernate-tx-programmatic">
|
||
|
|
<title>Programmatic transaction demarcation</title>
|
||
|
|
|
||
|
|
<para>Transactions can be demarcated in a higher level of the
|
||
|
|
application, on top of such lower-level data access services 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 <classname>PlatformTransactionManager</classname>. Again,
|
||
|
|
the latter can come from anywhere, but preferably as bean reference via
|
||
|
|
a <methodname>setTransactionManager(..)</methodname> method - just like
|
||
|
|
the <classname>productDAO</classname> should be set via a
|
||
|
|
<methodname>setProductDao(..)</methodname> 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.</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<bean id="myTxManager" class="org.springframework.orm.hibernate3.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></programlisting>
|
||
|
|
|
||
|
|
<programlisting>public class ProductServiceImpl implements ProductService {
|
||
|
|
|
||
|
|
private TransactionTemplate transactionTemplate;
|
||
|
|
private ProductDao productDao;
|
||
|
|
|
||
|
|
public void setTransactionManager(PlatformTransactionManager transactionManager) {
|
||
|
|
this.transactionTemplate = new TransactionTemplate(transactionManager);
|
||
|
|
}
|
||
|
|
|
||
|
|
public void setProductDao(ProductDao productDao) {
|
||
|
|
this.productDao = productDao;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void increasePriceOfAllProductsInCategory(final String category) {
|
||
|
|
this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
|
||
|
|
|
||
|
|
public void doInTransactionWithoutResult(TransactionStatus status) {
|
||
|
|
List productsToChange = this.productDao.loadProductsByCategory(category);
|
||
|
|
<lineannotation>// do the price increase...</lineannotation>
|
||
|
|
}
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-hibernate-tx-declarative">
|
||
|
|
<title>Declarative transaction demarcation</title>
|
||
|
|
|
||
|
|
<para>Alternatively, one can use Spring's declarative transaction
|
||
|
|
support, which essentially enables you to replace explicit transaction
|
||
|
|
demarcation API calls in your Java code with an AOP transaction
|
||
|
|
interceptor configured in a Spring container. This allows you to keep
|
||
|
|
business services free of repetitive transaction demarcation code, and
|
||
|
|
allows you to focus on adding business logic which is where the real
|
||
|
|
value of your application lies. Furthermore, transaction semantics like
|
||
|
|
propagation behavior and isolation level can be changed in a
|
||
|
|
configuration file and do not affect the business service
|
||
|
|
implementations.</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<bean id="myTxManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
|
||
|
|
<property name="sessionFactory" ref="mySessionFactory"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
<bean id="myProductService" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||
|
|
<property name="proxyInterfaces" value="product.ProductService"/>
|
||
|
|
<property name="target">
|
||
|
|
<bean class="product.DefaultProductService">
|
||
|
|
<property name="productDao" ref="myProductDao"/>
|
||
|
|
</bean>
|
||
|
|
</property>
|
||
|
|
<property name="interceptorNames">
|
||
|
|
<list>
|
||
|
|
<value>myTxInterceptor</value> <lineannotation><!-- the transaction interceptor (configured elsewhere) --></lineannotation>
|
||
|
|
</list>
|
||
|
|
</property>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<programlisting>public class ProductServiceImpl implements ProductService {
|
||
|
|
|
||
|
|
private ProductDao productDao;
|
||
|
|
|
||
|
|
public void setProductDao(ProductDao productDao) {
|
||
|
|
this.productDao = productDao;
|
||
|
|
}
|
||
|
|
|
||
|
|
<lineannotation>// notice the absence of transaction demarcation code in this method</lineannotation>
|
||
|
|
<lineannotation>// Spring's declarative transaction infrastructure will be demarcating transactions on your behalf </lineannotation>
|
||
|
|
public void increasePriceOfAllProductsInCategory(final String category) {
|
||
|
|
List productsToChange = this.productDao.loadProductsByCategory(category);
|
||
|
|
<lineannotation>// ...</lineannotation>
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>Spring's <classname>TransactionInterceptor</classname> allows any
|
||
|
|
checked application exception to be thrown with the callback code, while
|
||
|
|
<classname>TransactionTemplate</classname> is restricted to unchecked
|
||
|
|
exceptions within the callback.
|
||
|
|
<classname>TransactionTemplate</classname> will trigger a rollback in
|
||
|
|
case of an unchecked application exception, or if the transaction has
|
||
|
|
been marked rollback-only by the application (via
|
||
|
|
<classname>TransactionStatus</classname>).
|
||
|
|
<classname>TransactionInterceptor</classname> behaves the same way by
|
||
|
|
default but allows configurable rollback policies per method.</para>
|
||
|
|
|
||
|
|
<para>The following higher level approach to declarative transactions
|
||
|
|
doesn't use the <classname>ProxyFactoryBean</classname>, and as such may
|
||
|
|
be easier to use if you have a large number of service objects that you
|
||
|
|
wish to make transactional.</para>
|
||
|
|
|
||
|
|
<note>
|
||
|
|
<para>You are <emphasis>strongly</emphasis> encouraged to read the
|
||
|
|
section entitled <xref linkend="transaction-declarative" /> if you
|
||
|
|
have not done so already prior to continuing.</para>
|
||
|
|
</note>
|
||
|
|
|
||
|
|
<programlisting><?xml version="1.0" encoding="UTF-8"?>
|
||
|
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
|
|
xmlns:aop="http://www.springframework.org/schema/aop"
|
||
|
|
xmlns:tx="http://www.springframework.org/schema/tx"
|
||
|
|
xsi:schemaLocation="
|
||
|
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
|
||
|
|
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
|
||
|
|
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
|
||
|
|
|
||
|
|
<lineannotation><!-- <interfacename>SessionFactory</interfacename>, <interfacename>DataSource</interfacename>, etc. omitted --></lineannotation>
|
||
|
|
|
||
|
|
<bean id="myTxManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
|
||
|
|
<property name="sessionFactory" ref="mySessionFactory"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
<aop:config>
|
||
|
|
<aop:pointcut id="productServiceMethods" expression="execution(* product.ProductService.*(..))"/>
|
||
|
|
<aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/>
|
||
|
|
</aop:config>
|
||
|
|
|
||
|
|
<tx:advice id="txAdvice" transaction-manager="myTxManager">
|
||
|
|
<tx:attributes>
|
||
|
|
<tx:method name="increasePrice*" propagation="REQUIRED"/>
|
||
|
|
<tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/>
|
||
|
|
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
|
||
|
|
</tx:attributes>
|
||
|
|
</tx:advice>
|
||
|
|
|
||
|
|
<bean id="myProductService" class="product.SimpleProductService">
|
||
|
|
<property name="productDao" ref="myProductDao"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-hibernate-tx-strategies">
|
||
|
|
<title>Transaction management strategies</title>
|
||
|
|
|
||
|
|
<para>Both <classname>TransactionTemplate</classname> and
|
||
|
|
<classname>TransactionInterceptor</classname> delegate the actual
|
||
|
|
transaction handling to a
|
||
|
|
<classname>PlatformTransactionManager</classname> instance, which can be
|
||
|
|
a <classname>HibernateTransactionManager</classname> (for a single
|
||
|
|
Hibernate <interfacename>SessionFactory</interfacename>, using a
|
||
|
|
<classname>ThreadLocal</classname>
|
||
|
|
<interfacename>Session</interfacename> under the hood) or a
|
||
|
|
<classname>JtaTransactionManager</classname> (delegating to the JTA
|
||
|
|
subsystem of the container) for Hibernate applications. You could even
|
||
|
|
use a custom <classname>PlatformTransactionManager</classname>
|
||
|
|
implementation. So switching from native Hibernate transaction
|
||
|
|
management to JTA, such as 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.</para>
|
||
|
|
|
||
|
|
<para>For distributed transactions across multiple Hibernate session
|
||
|
|
factories, simply combine <classname>JtaTransactionManager</classname>
|
||
|
|
as a transaction strategy with multiple
|
||
|
|
<classname>LocalSessionFactoryBean</classname> definitions. Each of your
|
||
|
|
DAOs then gets one specific
|
||
|
|
<interfacename>SessionFactory</interfacename> reference passed into its
|
||
|
|
corresponding 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
|
||
|
|
<classname>JtaTransactionManager</classname> as the strategy.</para>
|
||
|
|
|
||
|
|
<programlisting><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.hibernate3.LocalSessionFactoryBean">
|
||
|
|
<property name="dataSource" ref="myDataSource1"/>
|
||
|
|
<property name="mappingResources">
|
||
|
|
<list>
|
||
|
|
<value>product.hbm.xml</value>
|
||
|
|
</list>
|
||
|
|
</property>
|
||
|
|
<property name="hibernateProperties">
|
||
|
|
<value>
|
||
|
|
hibernate.dialect=org.hibernate.dialect.MySQLDialect
|
||
|
|
hibernate.show_sql=true
|
||
|
|
</value>
|
||
|
|
</property>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
<bean id="mySessionFactory2" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
|
||
|
|
<property name="dataSource" ref="myDataSource2"/>
|
||
|
|
<property name="mappingResources">
|
||
|
|
<list>
|
||
|
|
<value>inventory.hbm.xml</value>
|
||
|
|
</list>
|
||
|
|
</property>
|
||
|
|
<property name="hibernateProperties">
|
||
|
|
<value>
|
||
|
|
hibernate.dialect=org.hibernate.dialect.OracleDialect
|
||
|
|
</value>
|
||
|
|
</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>
|
||
|
|
|
||
|
|
<lineannotation><!-- this shows the Spring 1.x style of declarative transaction configuration --></lineannotation>
|
||
|
|
<lineannotation><!-- it is totally supported, 100% legal in Spring 2.x, but see also above for the sleeker, Spring 2.0 style --></lineannotation>
|
||
|
|
<bean id="myProductService"
|
||
|
|
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
|
||
|
|
<property name="transactionManager" ref="myTxManager"/>
|
||
|
|
<property name="target">
|
||
|
|
<bean class="product.ProductServiceImpl">
|
||
|
|
<property name="productDao" ref="myProductDao"/>
|
||
|
|
<property name="inventoryDao" ref="myInventoryDao"/>
|
||
|
|
</bean>
|
||
|
|
</property>
|
||
|
|
<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></programlisting>
|
||
|
|
|
||
|
|
<para>Both <classname>HibernateTransactionManager</classname> and
|
||
|
|
<classname>JtaTransactionManager</classname> 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).</para>
|
||
|
|
|
||
|
|
<para><classname>HibernateTransactionManager</classname> can export the
|
||
|
|
JDBC <interfacename>Connection</interfacename> used by Hibernate to
|
||
|
|
plain JDBC access code, for a specific
|
||
|
|
<interfacename>DataSource</interfacename>. This allows for high-level
|
||
|
|
transaction demarcation with mixed Hibernate/JDBC data access completely
|
||
|
|
without JTA, as long as you are just accessing one database!
|
||
|
|
<classname>HibernateTransactionManager</classname> will automatically
|
||
|
|
expose the Hibernate transaction as JDBC transaction if the passed-in
|
||
|
|
<interfacename>SessionFactory</interfacename> has been set up with a
|
||
|
|
<interfacename>DataSource</interfacename> (through the "dataSource"
|
||
|
|
property of the <classname>LocalSessionFactoryBean</classname> class).
|
||
|
|
Alternatively, the <interfacename>DataSource</interfacename> that the
|
||
|
|
transactions are supposed to be exposed for can also be specified
|
||
|
|
explicitly, through the "dataSource" property of the
|
||
|
|
<classname>HibernateTransactionManager</classname> class.</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-hibernate-resources">
|
||
|
|
<title>Container resources versus local resources</title>
|
||
|
|
|
||
|
|
<para>Spring's resource management allows for simple switching between a
|
||
|
|
JNDI <interfacename>SessionFactory</interfacename> and a local one,
|
||
|
|
without having to change a single line of application code. The decision
|
||
|
|
as to 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
|
||
|
|
<interfacename>SessionFactory</interfacename>, a manually registered
|
||
|
|
JNDI <interfacename>SessionFactory</interfacename> does not provide any
|
||
|
|
benefits. Deploying a <interfacename>SessionFactory</interfacename>
|
||
|
|
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.</para>
|
||
|
|
|
||
|
|
<para>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 <interfacename>DataSource</interfacename> instances. For
|
||
|
|
non-Spring JTA-driven Hibernate transactions, you have to use the
|
||
|
|
Hibernate JCA connector, or extra Hibernate transaction code with the
|
||
|
|
<interfacename>TransactionManagerLookup</interfacename> being configured
|
||
|
|
for proper JVM-level caching.</para>
|
||
|
|
|
||
|
|
<para>Spring-driven transactions can work with a locally defined
|
||
|
|
Hibernate <interfacename>SessionFactory</interfacename> nicely, just
|
||
|
|
like with a local JDBC <interfacename>DataSource</interfacename> - 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 for example
|
||
|
|
WebLogic Express does not provide JCA. A Spring application 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.</para>
|
||
|
|
|
||
|
|
<para>All things considered: if you do not use EJB, stick with local
|
||
|
|
<interfacename>SessionFactory</interfacename> setup and Spring's
|
||
|
|
<classname>HibernateTransactionManager</classname> or
|
||
|
|
<classname>JtaTransactionManager</classname>. You will get all of the
|
||
|
|
benefits including proper transactional JVM-level caching and
|
||
|
|
distributed transactions, without any container deployment hassle. JNDI
|
||
|
|
registration of a Hibernate
|
||
|
|
<interfacename>SessionFactory</interfacename> via the JCA connector
|
||
|
|
really only adds value when used in conjunction with EJBs.</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-hibernate-invalid-jdbc-access-error">
|
||
|
|
<title>Spurious application server warnings when using Hibernate</title>
|
||
|
|
|
||
|
|
<para>In some JTA environments with very strict
|
||
|
|
<interfacename>XADataSource</interfacename> implementations -- currently
|
||
|
|
only some WebLogic and WebSphere versions -- when using Hibernate
|
||
|
|
configured without any awareness of the JTA
|
||
|
|
<interfacename>PlatformTransactionManager</interfacename> object for
|
||
|
|
that environment, it 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:</para>
|
||
|
|
|
||
|
|
<programlisting>java.sql.SQLException: The transaction is no longer active - status: 'Committed'.
|
||
|
|
No further JDBC access is allowed within this transaction.</programlisting>
|
||
|
|
|
||
|
|
<para>This warning is easy to resolve by simply making Hibernate aware
|
||
|
|
of the JTA <interfacename>PlatformTransactionManager</interfacename>
|
||
|
|
instance, to which it will also synchronize (along with Spring). This
|
||
|
|
may be done in two ways:</para>
|
||
|
|
|
||
|
|
<itemizedlist>
|
||
|
|
<listitem>
|
||
|
|
<para>If in your application context you are already directly
|
||
|
|
obtaining the JTA
|
||
|
|
<interfacename>PlatformTransactionManager</interfacename> object
|
||
|
|
(presumably from JNDI via <literal>JndiObjectFactoryBean</literal>)
|
||
|
|
and feeding it for example to Spring's
|
||
|
|
<classname>JtaTransactionManager</classname>, then the easiest way
|
||
|
|
is to simply specify a reference to this as the value of
|
||
|
|
<classname>LocalSessionFactoryBean</classname>'s
|
||
|
|
<emphasis>jtaTransactionManager</emphasis> property. Spring will
|
||
|
|
then make the object available to Hibernate.</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>More likely you do not already have the JTA
|
||
|
|
<interfacename>PlatformTransactionManager</interfacename> instance
|
||
|
|
(since Spring's <classname>JtaTransactionManager</classname> 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 <literal>TransactionManagerLookup</literal> class in the
|
||
|
|
Hibernate configuration, as described in the Hibernate
|
||
|
|
manual.</para>
|
||
|
|
</listitem>
|
||
|
|
</itemizedlist>
|
||
|
|
|
||
|
|
<para>It is not necessary to read any more for proper usage, but the
|
||
|
|
full sequence of events with and without Hibernate being aware of the
|
||
|
|
JTA <interfacename>PlatformTransactionManager</interfacename> will now
|
||
|
|
be described.</para>
|
||
|
|
|
||
|
|
<para>When Hibernate is not configured with any awareness of the JTA
|
||
|
|
<interfacename>PlatformTransactionManager</interfacename>, the sequence
|
||
|
|
of events when a JTA transaction commits is as follows:</para>
|
||
|
|
|
||
|
|
<itemizedlist>
|
||
|
|
<listitem>
|
||
|
|
<para>JTA transaction commits</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>Spring's <classname>JtaTransactionManager</classname> is
|
||
|
|
synchronized to the JTA transaction, so it is called back via an
|
||
|
|
<emphasis>afterCompletion</emphasis> callback by the JTA transaction
|
||
|
|
manager.</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>Among other activities, this can trigger a callback by Spring
|
||
|
|
to Hibernate, via Hibernate's
|
||
|
|
<literal>afterTransactionCompletion</literal> callback (used to
|
||
|
|
clear the Hibernate cache), followed by an explicit
|
||
|
|
<literal>close()</literal> call on the Hibernate Session, which
|
||
|
|
results in Hibernate trying to <literal>close()</literal> the JDBC
|
||
|
|
Connection.</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>In some environments, this
|
||
|
|
<methodname>Connection.close()</methodname> call then triggers the
|
||
|
|
warning or error, as the application server no longer considers the
|
||
|
|
<interfacename>Connection</interfacename> usable at all, since the
|
||
|
|
transaction has already been committed.</para>
|
||
|
|
</listitem>
|
||
|
|
</itemizedlist>
|
||
|
|
|
||
|
|
<para>When Hibernate is configured with awareness of the JTA
|
||
|
|
<interfacename>PlatformTransactionManager</interfacename>, the sequence
|
||
|
|
of events when a JTA transaction commits is instead as follows:</para>
|
||
|
|
|
||
|
|
<itemizedlist>
|
||
|
|
<listitem>
|
||
|
|
<para>JTA transaction is ready to commit</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>Spring's <classname>JtaTransactionManager</classname> is
|
||
|
|
synchronized to the JTA transaction, so it is called back via a
|
||
|
|
<emphasis>beforeCompletion</emphasis> callback by the JTA
|
||
|
|
transaction manager.</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>Spring is aware that Hibernate itself is synchronized to the
|
||
|
|
JTA transaction, and behaves differently than in the previous
|
||
|
|
scenario. Assuming the Hibernate
|
||
|
|
<interfacename>Session</interfacename> needs to be closed at all,
|
||
|
|
Spring will close it now.</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>JTA Transaction commits</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>Hibernate is synchronized to the JTA transaction, so it is
|
||
|
|
called back via an <emphasis>afterCompletion</emphasis> callback by
|
||
|
|
the JTA transaction manager, and can properly clear its
|
||
|
|
cache.</para>
|
||
|
|
</listitem>
|
||
|
|
</itemizedlist>
|
||
|
|
|
||
|
|
<para></para>
|
||
|
|
</section>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-jdo">
|
||
|
|
<title>JDO</title>
|
||
|
|
|
||
|
|
<para>Spring supports the standard JDO 2.0/2.1 API as data access
|
||
|
|
strategy, following the same style as the Hibernate support. The
|
||
|
|
corresponding integration classes reside in the
|
||
|
|
<literal>org.springframework.orm.jdo</literal> package.</para>
|
||
|
|
|
||
|
|
<section id="orm-jdo-setup">
|
||
|
|
<title><interfacename>PersistenceManagerFactory</interfacename>
|
||
|
|
setup</title>
|
||
|
|
|
||
|
|
<para>Spring provides a
|
||
|
|
<classname>LocalPersistenceManagerFactoryBean</classname> class that
|
||
|
|
allows for defining a local JDO
|
||
|
|
<interfacename>PersistenceManagerFactory</interfacename> within a Spring
|
||
|
|
application context:</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<bean id="myPmf" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean">
|
||
|
|
<property name="configLocation" value="classpath:kodo.properties"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<para>Alternatively, a
|
||
|
|
<interfacename>PersistenceManagerFactory</interfacename> can also be set
|
||
|
|
up through direct instantiation of a
|
||
|
|
<interfacename>PersistenceManagerFactory</interfacename> implementation
|
||
|
|
class. A JDO <interfacename>PersistenceManagerFactory</interfacename>
|
||
|
|
implementation class is supposed to follow the JavaBeans pattern, just
|
||
|
|
like a JDBC <interfacename>DataSource</interfacename> implementation
|
||
|
|
class, which is a natural fit for a Spring bean definition. This setup
|
||
|
|
style usually supports a Spring-defined JDBC
|
||
|
|
<interfacename>DataSource</interfacename>, passed into the
|
||
|
|
"connectionFactory" property. For example, for the open source JDO
|
||
|
|
implementation JPOX (<ulink url="http://www.jpox.org"></ulink>):</para>
|
||
|
|
|
||
|
|
<programlisting><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></programlisting>
|
||
|
|
|
||
|
|
<para>A JDO <interfacename>PersistenceManagerFactory</interfacename> 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
|
||
|
|
<literal>JndiObjectFactoryBean</literal> can be used to retrieve and
|
||
|
|
expose such a <interfacename>PersistenceManagerFactory</interfacename>.
|
||
|
|
However, outside an EJB context, there is often no compelling benefit in
|
||
|
|
holding the <interfacename>PersistenceManagerFactory</interfacename> 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.</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-jdo-template">
|
||
|
|
<title><classname>JdoTemplate</classname> and
|
||
|
|
<classname>JdoDaoSupport</classname></title>
|
||
|
|
|
||
|
|
<para>Each JDO-based DAO will then receive the
|
||
|
|
<interfacename>PersistenceManagerFactory</interfacename> through
|
||
|
|
dependency injection. Such a DAO could be coded against plain JDO API,
|
||
|
|
working with the given
|
||
|
|
<interfacename>PersistenceManagerFactory</interfacename>, but will
|
||
|
|
usually rather be used with the Spring Framework's
|
||
|
|
<classname>JdoTemplate</classname>:</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<bean id="myProductDao" class="product.ProductDaoImpl">
|
||
|
|
<property name="persistenceManagerFactory" ref="myPmf"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<programlisting>public class ProductDaoImpl implements ProductDao {
|
||
|
|
|
||
|
|
private JdoTemplate jdoTemplate;
|
||
|
|
|
||
|
|
public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) {
|
||
|
|
this.jdoTemplate = new JdoTemplate(pmf);
|
||
|
|
}
|
||
|
|
|
||
|
|
public Collection loadProductsByCategory(final String category) throws DataAccessException {
|
||
|
|
return (Collection) this.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);
|
||
|
|
<lineannotation>// do some further stuff with the result list</lineannotation>
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>A callback implementation can effectively be used for any JDO data
|
||
|
|
access. <classname>JdoTemplate</classname> will ensure that
|
||
|
|
<classname>PersistenceManager</classname>s 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 <literal>find</literal>,
|
||
|
|
<literal>load</literal>, <literal>makePersistent</literal>, or
|
||
|
|
<literal>delete</literal> call, <classname>JdoTemplate</classname>
|
||
|
|
offers alternative convenience methods that can replace such one line
|
||
|
|
callback implementations. Furthermore, Spring provides a convenient
|
||
|
|
<classname>JdoDaoSupport</classname> base class that provides a
|
||
|
|
<literal>setPersistenceManagerFactory(..)</literal> method for receiving
|
||
|
|
a <classname>PersistenceManagerFactory</classname>, and
|
||
|
|
<methodname>getPersistenceManagerFactory()</methodname> and
|
||
|
|
<methodname>getJdoTemplate()</methodname> for use by subclasses. In
|
||
|
|
combination, this allows for very simple DAO implementations for typical
|
||
|
|
requirements:</para>
|
||
|
|
|
||
|
|
<programlisting>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});
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>As alternative to working with Spring's
|
||
|
|
<classname>JdoTemplate</classname>, you can also code Spring-based DAOs
|
||
|
|
at the JDO API level, explicitly opening and closing a
|
||
|
|
<interfacename>PersistenceManager</interfacename>. 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.
|
||
|
|
<classname>JdoDaoSupport</classname> offers a variety of support methods
|
||
|
|
for this scenario, for fetching and releasing a transactional
|
||
|
|
<interfacename>PersistenceManager</interfacename> as well as for
|
||
|
|
converting exceptions.</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-jdo-daos-straight">
|
||
|
|
<title>Implementing DAOs based on the plain JDO API</title>
|
||
|
|
|
||
|
|
<para>DAOs can also be written against plain JDO API, without any Spring
|
||
|
|
dependencies, directly using an injected
|
||
|
|
<interfacename>PersistenceManagerFactory</interfacename>. A
|
||
|
|
corresponding DAO implementation looks like as follows:</para>
|
||
|
|
|
||
|
|
<programlisting>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();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>As the above DAO still follows the Dependency Injection pattern,
|
||
|
|
it still fits nicely into a Spring container, just like it would if
|
||
|
|
coded against Spring's <classname>JdoTemplate</classname>:</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<bean id="myProductDao" class="product.ProductDaoImpl">
|
||
|
|
<property name="persistenceManagerFactory" ref="myPmf"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<para>The main issue with such DAOs is that they always get a new
|
||
|
|
<interfacename>PersistenceManager</interfacename> from the factory. To
|
||
|
|
still access a Spring-managed transactional
|
||
|
|
<interfacename>PersistenceManager</interfacename>, consider defining a
|
||
|
|
<classname>TransactionAwarePersistenceManagerFactoryProxy</classname>
|
||
|
|
(as included in Spring) in front of your target
|
||
|
|
<interfacename>PersistenceManagerFactory</interfacename>, passing the
|
||
|
|
proxy into your DAOs.</para>
|
||
|
|
|
||
|
|
<programlisting><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></programlisting>
|
||
|
|
|
||
|
|
<para>Your data access code will then receive a transactional
|
||
|
|
<interfacename>PersistenceManager</interfacename> (if any) from the
|
||
|
|
<methodname>PersistenceManagerFactory.getPersistenceManager()</methodname>
|
||
|
|
method that it calls. The latter method call goes through the proxy,
|
||
|
|
which will first check for a current transactional
|
||
|
|
<interfacename>PersistenceManager</interfacename> before getting a new
|
||
|
|
one from the factory. <methodname>close()</methodname> calls on the
|
||
|
|
<interfacename>PersistenceManager</interfacename> will be ignored in
|
||
|
|
case of a transactional
|
||
|
|
<interfacename>PersistenceManager</interfacename>.</para>
|
||
|
|
|
||
|
|
<para>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 <methodname>PersistenceManager.close()</methodname>
|
||
|
|
call and thus the entire <literal>finally</literal> block, which you
|
||
|
|
might prefer to keep your DAO implementations concise:</para>
|
||
|
|
|
||
|
|
<programlisting>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);
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>With such DAOs that rely on active transactions, it is recommended
|
||
|
|
to enforce active transactions through turning
|
||
|
|
<classname>TransactionAwarePersistenceManagerFactoryProxy</classname>'s
|
||
|
|
"allowCreate" flag off:</para>
|
||
|
|
|
||
|
|
<programlisting><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></programlisting>
|
||
|
|
|
||
|
|
<para>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.</para>
|
||
|
|
|
||
|
|
<para>However, the DAO throws plain
|
||
|
|
<exceptionname>JDOException</exceptionname> (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.</para>
|
||
|
|
|
||
|
|
<para>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 familiar with JDO, feeling
|
||
|
|
more natural to them. However, such DAOs will throw plain
|
||
|
|
<exceptionname>JDOException</exceptionname>; conversion to Spring's
|
||
|
|
<exceptionname>DataAccessException</exceptionname> would have to happen
|
||
|
|
explicitly (if desired).</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-jdo-tx">
|
||
|
|
<title>Transaction management</title>
|
||
|
|
|
||
|
|
<para>To execute service operations within transactions, you can use
|
||
|
|
Spring's common declarative transaction facilities. For example:</para>
|
||
|
|
|
||
|
|
<programlisting><?xml version="1.0" encoding="UTF-8"?>
|
||
|
|
<beans
|
||
|
|
xmlns="http://www.springframework.org/schema/beans"
|
||
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
|
|
xmlns:aop="http://www.springframework.org/schema/aop"
|
||
|
|
xmlns:tx="http://www.springframework.org/schema/tx"
|
||
|
|
xsi:schemaLocation="
|
||
|
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
|
||
|
|
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
|
||
|
|
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
|
||
|
|
|
||
|
|
<bean id="myTxManager" class="org.springframework.orm.jdo.JdoTransactionManager">
|
||
|
|
<property name="persistenceManagerFactory" ref="myPmf"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
<bean id="myProductService" class="product.ProductServiceImpl">
|
||
|
|
<property name="productDao" ref="myProductDao"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
<tx:advice id="txAdvice" transaction-manager="txManager">
|
||
|
|
<tx:attributes>
|
||
|
|
<tx:method name="increasePrice*" propagation="REQUIRED"/>
|
||
|
|
<tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/>
|
||
|
|
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
|
||
|
|
</tx:attributes>
|
||
|
|
</tx:advice>
|
||
|
|
|
||
|
|
<aop:config>
|
||
|
|
<aop:pointcut id="productServiceMethods" expression="execution(* product.ProductService.*(..))"/>
|
||
|
|
<aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/>
|
||
|
|
</aop:config>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<para>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
|
||
|
|
<classname>JdoTransactionManager</classname>, but it is necessary for
|
||
|
|
participating in JTA transactions (whether driven by Spring's
|
||
|
|
<classname>JtaTransactionManager</classname> or by EJB CMT / plain
|
||
|
|
JTA).</para>
|
||
|
|
|
||
|
|
<para><classname>JdoTransactionManager</classname> is capable of
|
||
|
|
exposing a JDO transaction to JDBC access code that accesses the same
|
||
|
|
JDBC <interfacename>DataSource</interfacename>, provided that the
|
||
|
|
registered <classname>JdoDialect</classname> supports retrieval of the
|
||
|
|
underlying JDBC <interfacename>Connection</interfacename>. This is
|
||
|
|
the case for JDBC-based JDO 2.0 implementations by default.</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-jdo-dialect">
|
||
|
|
<title><interfacename>JdoDialect</interfacename></title>
|
||
|
|
|
||
|
|
<para>As an advanced feature, both <classname>JdoTemplate</classname>
|
||
|
|
and <classname>interfacename</classname> support a custom
|
||
|
|
<interfacename>JdoDialect</interfacename>, to be passed into the
|
||
|
|
"jdoDialect" bean property. In such a scenario, the DAOs won't receive a
|
||
|
|
<interfacename>PersistenceManagerFactory</interfacename> reference but
|
||
|
|
rather a full <classname>JdoTemplate</classname> instance instead (for
|
||
|
|
example, passed into <classname>JdoDaoSupport</classname>'s
|
||
|
|
"jdoTemplate" property). A <interfacename>JdoDialect</interfacename>
|
||
|
|
implementation can enable some advanced features supported by Spring,
|
||
|
|
usually in a vendor-specific manner:</para>
|
||
|
|
|
||
|
|
<itemizedlist>
|
||
|
|
<listitem>
|
||
|
|
<para>applying specific transaction semantics (such as custom
|
||
|
|
isolation level or transaction timeout)</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>retrieving the transactional JDBC
|
||
|
|
<interfacename>Connection</interfacename> (for exposure to
|
||
|
|
JDBC-based DAOs)</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>applying query timeouts (automatically calculated from
|
||
|
|
Spring-managed transaction timeout)</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>eagerly flushing a
|
||
|
|
<interfacename>PersistenceManager</interfacename> (to make
|
||
|
|
transactional changes visible to JDBC-based data access code)</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>advanced translation of <literal>JDOExceptions</literal> to
|
||
|
|
Spring <literal>DataAccessExceptions</literal></para>
|
||
|
|
</listitem>
|
||
|
|
</itemizedlist>
|
||
|
|
|
||
|
|
<para>See the <classname>JdoDialect</classname> Javadoc for more details
|
||
|
|
on its operations and how they are used within Spring's JDO support.</para>
|
||
|
|
</section>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-toplink">
|
||
|
|
<title>Oracle TopLink</title>
|
||
|
|
|
||
|
|
<para>Since Spring 1.2, Spring supports Oracle TopLink (<ulink
|
||
|
|
url="http://www.oracle.com/technology/products/ias/toplink"></ulink>) 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
|
||
|
|
<literal>org.springframework.orm.toplink</literal> package.</para>
|
||
|
|
|
||
|
|
<para>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!</para>
|
||
|
|
|
||
|
|
<section id="orm-toplink-abstraction">
|
||
|
|
<title><interfacename>SessionFactory</interfacename> abstraction</title>
|
||
|
|
|
||
|
|
<para>TopLink itself does not ship with a SessionFactory abstraction.
|
||
|
|
Instead, multi-threaded access is based on the concept of a central
|
||
|
|
<literal>ServerSession</literal>, which in turn is able to spawn
|
||
|
|
<literal>ClientSession</literal> instances for single-threaded usage.
|
||
|
|
For flexible setup options, Spring defines a
|
||
|
|
<interfacename>SessionFactory</interfacename> abstraction for TopLink,
|
||
|
|
enabling to switch between different
|
||
|
|
<interfacename>Session</interfacename> creation strategies.</para>
|
||
|
|
|
||
|
|
<para>As a one-stop shop, Spring provides a
|
||
|
|
<classname>LocalSessionFactoryBean</classname> class that allows for
|
||
|
|
defining a TopLink <interfacename>SessionFactory</interfacename> 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 <interfacename>DataSource</interfacename> to
|
||
|
|
use.</para>
|
||
|
|
|
||
|
|
<programlisting><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></programlisting>
|
||
|
|
|
||
|
|
<programlisting><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></programlisting>
|
||
|
|
|
||
|
|
<para>Usually, <classname>LocalSessionFactoryBean</classname> will hold
|
||
|
|
a multi-threaded TopLink <literal>ServerSession</literal> underneath and
|
||
|
|
create appropriate client <interfacename>Session</interfacename>s for
|
||
|
|
it: either a plain <interfacename>Session</interfacename> (typical), a
|
||
|
|
managed <literal>ClientSession</literal>, or a transaction-aware
|
||
|
|
<interfacename>Session</interfacename> (the latter are mainly used
|
||
|
|
internally by Spring's TopLink support). It might also hold a
|
||
|
|
single-threaded TopLink <literal>DatabaseSession</literal>; this is
|
||
|
|
rather unusual, though.</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-toplink-template">
|
||
|
|
<title><classname>TopLinkTemplate</classname> and
|
||
|
|
<classname>TopLinkDaoSupport</classname></title>
|
||
|
|
|
||
|
|
<para>Each TopLink-based DAO will then receive the
|
||
|
|
<interfacename>SessionFactory</interfacename> 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 <interfacename>Session</interfacename> from the given
|
||
|
|
<interfacename>SessionFactory</interfacename>, but will usually rather
|
||
|
|
be used with Spring's <literal>TopLinkTemplate</literal>:</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<bean id="myProductDao" class="product.ProductDaoImpl">
|
||
|
|
<property name="sessionFactory" ref="mySessionFactory"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<programlisting>public class TopLinkProductDao implements ProductDao {
|
||
|
|
|
||
|
|
private TopLinkTemplate tlTemplate;
|
||
|
|
|
||
|
|
public void setSessionFactory(SessionFactory sessionFactory) {
|
||
|
|
this.tlTemplate = new TopLinkTemplate(sessionFactory);
|
||
|
|
}
|
||
|
|
|
||
|
|
public Collection loadProductsByCategory(final String category) throws DataAccessException {
|
||
|
|
return (Collection) this.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);
|
||
|
|
<lineannotation>// do some further stuff with the result list</lineannotation>
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>A callback implementation can effectively be used for any TopLink
|
||
|
|
data access. <classname>TopLinkTemplate</classname> will ensure that
|
||
|
|
<interfacename>Session</interfacename>s 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 <literal>executeQuery</literal>, <literal>readAll</literal>,
|
||
|
|
<literal>readById</literal>, or <literal>merge</literal> call,
|
||
|
|
<classname>JdoTemplate</classname> offers alternative convenience
|
||
|
|
methods that can replace such one line callback implementations.
|
||
|
|
Furthermore, Spring provides a convenient
|
||
|
|
<classname>TopLinkDaoSupport</classname> base class that provides a
|
||
|
|
<literal>setSessionFactory(..)</literal> method for receiving a
|
||
|
|
<interfacename>SessionFactory</interfacename>, and
|
||
|
|
<literal>getSessionFactory()</literal> and
|
||
|
|
<literal>getTopLinkTemplate()</literal> for use by subclasses. In
|
||
|
|
combination, this allows for simple DAO implementations for typical
|
||
|
|
requirements:</para>
|
||
|
|
|
||
|
|
<programlisting>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});
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>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.</para>
|
||
|
|
|
||
|
|
<para>As alternative to working with Spring's
|
||
|
|
<classname>TopLinkTemplate</classname>, you can also code your TopLink
|
||
|
|
data access based on the raw TopLink API, explicitly opening and closing
|
||
|
|
a <interfacename>Session</interfacename>. 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.
|
||
|
|
<classname>TopLinkDaoSupport</classname> offers a variety of support
|
||
|
|
methods for this scenario, for fetching and releasing a transactional
|
||
|
|
<interfacename>Session</interfacename> as well as for converting
|
||
|
|
exceptions.</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-toplink-straight">
|
||
|
|
<title>Implementing DAOs based on plain TopLink API</title>
|
||
|
|
|
||
|
|
<para>DAOs can also be written against plain TopLink API, without any
|
||
|
|
Spring dependencies, directly using an injected TopLink
|
||
|
|
<interfacename>Session</interfacename>. The latter will usually be based
|
||
|
|
on a <interfacename>SessionFactory</interfacename> defined by a
|
||
|
|
<classname>LocalSessionFactoryBean</classname>, exposed for bean
|
||
|
|
references of type <interfacename>Session</interfacename> through
|
||
|
|
Spring's <literal>TransactionAwareSessionAdapter</literal>.</para>
|
||
|
|
|
||
|
|
<para>The <literal>getActiveSession()</literal> method defined on
|
||
|
|
TopLink's <interfacename>Session</interfacename> interface will return
|
||
|
|
the current transactional <interfacename>Session</interfacename> in such
|
||
|
|
a scenario. If there is no active transaction, it will return the shared
|
||
|
|
TopLink <literal>ServerSession</literal> as-is, which is only supposed
|
||
|
|
to be used directly for read-only access. There is also an analogous
|
||
|
|
<methodname>getActiveUnitOfWork()</methodname> method, returning the
|
||
|
|
TopLink <interfacename>UnitOfWork</interfacename> associated with the
|
||
|
|
current transaction, if any (returning <literal>null</literal>
|
||
|
|
else).</para>
|
||
|
|
|
||
|
|
<para>A corresponding DAO implementation looks like as follows:</para>
|
||
|
|
|
||
|
|
<programlisting>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);
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>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
|
||
|
|
<classname>TopLinkTemplate</classname>. Spring's
|
||
|
|
<literal>TransactionAwareSessionAdapter</literal> is used to expose a
|
||
|
|
bean reference of type <interfacename>Session</interfacename>, to be
|
||
|
|
passed into the DAO:</para>
|
||
|
|
|
||
|
|
<programlisting><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></programlisting>
|
||
|
|
|
||
|
|
<para>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.</para>
|
||
|
|
|
||
|
|
<para>However, the DAO throws plain
|
||
|
|
<exceptionname>TopLinkException</exceptionname> (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.</para>
|
||
|
|
|
||
|
|
<para>A further disadvantage of that DAO style is that TopLink's
|
||
|
|
standard <methodname>getActiveSession()</methodname> feature just works
|
||
|
|
within JTA transactions. It does <emphasis>not</emphasis> work with any
|
||
|
|
other transaction strategy out-of-the-box, in particular not with local
|
||
|
|
TopLink transactions.</para>
|
||
|
|
|
||
|
|
<para>Fortunately, Spring's
|
||
|
|
<literal>TransactionAwareSessionAdapter</literal> exposes a
|
||
|
|
corresponding proxy for the TopLink <literal>ServerSession</literal>
|
||
|
|
which supports TopLink's <literal>Session.getActiveSession()</literal>
|
||
|
|
and <literal>Session.getActiveUnitOfWork()</literal> methods for any
|
||
|
|
Spring transaction strategy, returning the current Spring-managed
|
||
|
|
transactional <interfacename>Session</interfacename> even with
|
||
|
|
<literal>TopLinkTransactionManager</literal>. Of course, the standard
|
||
|
|
behavior of that method remains: returning the current
|
||
|
|
<interfacename>Session</interfacename> associated with the ongoing JTA
|
||
|
|
transaction, if any (no matter whether driven by Spring's
|
||
|
|
<classname>JtaTransactionManager</classname>, by EJB CMT, or by plain
|
||
|
|
JTA).</para>
|
||
|
|
|
||
|
|
<para>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 familiar with TopLink,
|
||
|
|
feeling more natural to them. However, such DAOs will throw plain
|
||
|
|
<exceptionname>TopLinkException</exceptionname>; conversion to Spring's
|
||
|
|
<classname>DataAccessException</classname> would have to happen
|
||
|
|
explicitly (if desired).</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-toplink-tx">
|
||
|
|
<title>Transaction management</title>
|
||
|
|
|
||
|
|
<para>To execute service operations within transactions, you can use
|
||
|
|
Spring's common declarative transaction facilities. For example:</para>
|
||
|
|
|
||
|
|
<programlisting><?xml version="1.0" encoding="UTF-8"?>
|
||
|
|
<beans
|
||
|
|
xmlns="http://www.springframework.org/schema/beans"
|
||
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
|
|
xmlns:aop="http://www.springframework.org/schema/aop"
|
||
|
|
xmlns:tx="http://www.springframework.org/schema/tx"
|
||
|
|
xsi:schemaLocation="
|
||
|
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
|
||
|
|
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
|
||
|
|
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
|
||
|
|
|
||
|
|
<bean id="myTxManager" class="org.springframework.orm.toplink.TopLinkTransactionManager">
|
||
|
|
<property name="sessionFactory" ref="mySessionFactory"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
<bean id="myProductService" class="product.ProductServiceImpl">
|
||
|
|
<property name="productDao" ref="myProductDao"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
<aop:config>
|
||
|
|
<aop:pointcut id="productServiceMethods" expression="execution(* product.ProductService.*(..))"/>
|
||
|
|
<aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/>
|
||
|
|
</aop:config>
|
||
|
|
|
||
|
|
<tx:advice id="txAdvice" transaction-manager="myTxManager">
|
||
|
|
<tx:attributes>
|
||
|
|
<tx:method name="increasePrice*" propagation="REQUIRED"/>
|
||
|
|
<tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/>
|
||
|
|
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
|
||
|
|
</tx:attributes>
|
||
|
|
</tx:advice>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<para>Note that TopLink requires an active
|
||
|
|
<interfacename>UnitOfWork</interfacename> for modifying a persistent
|
||
|
|
object. (You should never modify objects returned by a plain TopLink
|
||
|
|
<interfacename>Session</interfacename> - 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 <interfacename>Session</interfacename> and
|
||
|
|
<interfacename>UnitOfWork</interfacename>. This is not necessary for
|
||
|
|
local transactions as performed by Spring's
|
||
|
|
<literal>TopLinkTransactionManager</literal>, but it is necessary for
|
||
|
|
participating in JTA transactions (whether driven by Spring's
|
||
|
|
<classname>JtaTransactionManager</classname> or by EJB CMT / plain
|
||
|
|
JTA).</para>
|
||
|
|
|
||
|
|
<para>Within your TopLink-based DAO code, use the
|
||
|
|
<literal>Session.getActiveUnitOfWork()</literal> method to access the
|
||
|
|
current <interfacename>UnitOfWork</interfacename> 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
|
||
|
|
<interfacename>UnitOfWork</interfacename> instances that won't
|
||
|
|
participate in the current transaction; this is hardly needed,
|
||
|
|
though.</para>
|
||
|
|
|
||
|
|
<para><classname>TopLinkTransactionManager</classname> is capable of
|
||
|
|
exposing a TopLink transaction to JDBC access code that accesses the
|
||
|
|
same JDBC <interfacename>DataSource</interfacename>, provided that
|
||
|
|
TopLink works with JDBC in the backend and is thus able to expose the
|
||
|
|
underlying JDBC <interfacename>Connection</interfacename>. The
|
||
|
|
<interfacename>DataSource</interfacename> to expose the transactions for
|
||
|
|
needs to be specified explicitly; it won't be autodetected.</para>
|
||
|
|
</section>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-ibatis">
|
||
|
|
<title>iBATIS SQL Maps</title>
|
||
|
|
|
||
|
|
<para>The iBATIS support in the Spring Framework 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.</para>
|
||
|
|
|
||
|
|
<para>Transaction management can be handled through Spring's standard
|
||
|
|
facilities. There are no special transaction strategies for iBATIS, as
|
||
|
|
there is no special transactional resource involved other than a JDBC
|
||
|
|
<interfacename>Connection</interfacename>. Hence, Spring's standard JDBC
|
||
|
|
<classname>DataSourceTransactionManager</classname> or
|
||
|
|
<classname>JtaTransactionManager</classname> are perfectly
|
||
|
|
sufficient.</para>
|
||
|
|
|
||
|
|
<note>
|
||
|
|
<para>Spring does actually support both iBatis 1.x and 2.x. However,
|
||
|
|
only support for iBatis 2.x is actually shipped with the core Spring
|
||
|
|
distribution. The iBatis 1.x support classes were moved to the Spring
|
||
|
|
Modules project as of Spring 2.0, and you are directed there for
|
||
|
|
documentation.</para>
|
||
|
|
</note>
|
||
|
|
|
||
|
|
<section id="orm-ibatis-setup">
|
||
|
|
<title>Setting up the <classname>SqlMapClient</classname></title>
|
||
|
|
|
||
|
|
<para>If we want to map the previous Account class with iBATIS 2.x we
|
||
|
|
need to create the following SQL map
|
||
|
|
<filename>'Account.xml'</filename>:</para>
|
||
|
|
|
||
|
|
<programlisting><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></programlisting>
|
||
|
|
|
||
|
|
<para>The configuration file for iBATIS 2 looks like this:</para>
|
||
|
|
|
||
|
|
<programlisting><sqlMapConfig>
|
||
|
|
|
||
|
|
<sqlMap resource="example/Account.xml"/>
|
||
|
|
|
||
|
|
</sqlMapConfig></programlisting>
|
||
|
|
|
||
|
|
<para>Remember that iBATIS loads resources from the class path, so be
|
||
|
|
sure to add the <filename>'Account.xml'</filename> file to the class
|
||
|
|
path.</para>
|
||
|
|
|
||
|
|
<para>We can use the <classname>SqlMapClientFactoryBean</classname> in
|
||
|
|
the Spring container. Note that with iBATIS SQL Maps 2.x, the JDBC
|
||
|
|
<interfacename>DataSource</interfacename> is usually specified on the
|
||
|
|
<classname>SqlMapClientFactoryBean</classname>, which enables lazy
|
||
|
|
loading.</para>
|
||
|
|
|
||
|
|
<programlisting><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></programlisting>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-ibatis-template">
|
||
|
|
<title>Using <classname>SqlMapClientTemplate</classname> and
|
||
|
|
<classname>SqlMapClientDaoSupport</classname></title>
|
||
|
|
|
||
|
|
<para>The <classname>SqlMapClientDaoSupport</classname> class offers a
|
||
|
|
supporting class similar to the <classname>SqlMapDaoSupport</classname>.
|
||
|
|
We extend it to implement our DAO:</para>
|
||
|
|
|
||
|
|
<programlisting>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);
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>In the DAO, we use the pre-configured
|
||
|
|
<classname>SqlMapClientTemplate</classname> to execute the queries,
|
||
|
|
after setting up the <literal>SqlMapAccountDao</literal> in the
|
||
|
|
application context and wiring it with our
|
||
|
|
<literal>SqlMapClient</literal> instance:</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<bean id="accountDao" class="example.SqlMapAccountDao">
|
||
|
|
<property name="sqlMapClient" ref="sqlMapClient"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<para>Note that a <classname>SqlMapTemplate</classname> instance could
|
||
|
|
also be created manually, passing in the <literal>SqlMapClient</literal>
|
||
|
|
as constructor argument. The <literal>SqlMapClientDaoSupport</literal>
|
||
|
|
base class simply pre-initializes a
|
||
|
|
<classname>SqlMapClientTemplate</classname> instance for us.</para>
|
||
|
|
|
||
|
|
<para>The <classname>SqlMapClientTemplate</classname> also offers a
|
||
|
|
generic <literal>execute</literal> method, taking a custom
|
||
|
|
<literal>SqlMapClientCallback</literal> implementation as argument. This
|
||
|
|
can, for example, be used for batching:</para>
|
||
|
|
|
||
|
|
<programlisting>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();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>In general, any combination of operations offered by the native
|
||
|
|
<literal>SqlMapExecutor</literal> API can be used in such a callback.
|
||
|
|
Any <literal>SQLException</literal> thrown will automatically get
|
||
|
|
converted to Spring's generic <classname>DataAccessException</classname>
|
||
|
|
hierarchy.</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-ibatis-straight">
|
||
|
|
<title>Implementing DAOs based on plain iBATIS API</title>
|
||
|
|
|
||
|
|
<para>DAOs can also be written against plain iBATIS API, without any
|
||
|
|
Spring dependencies, directly using an injected
|
||
|
|
<literal>SqlMapClient</literal>. A corresponding DAO implementation
|
||
|
|
looks like as follows:</para>
|
||
|
|
|
||
|
|
<programlisting>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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>In such a scenario, the <literal>SQLException</literal> 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:</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<bean id="accountDao" class="example.SqlMapAccountDao">
|
||
|
|
<property name="sqlMapClient" ref="sqlMapClient"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
</section>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-jpa">
|
||
|
|
<title>JPA</title>
|
||
|
|
|
||
|
|
<para>Spring JPA (available under the
|
||
|
|
<literal>org.springframework.orm.jpa</literal> package) offers
|
||
|
|
comprehensive support for the <ulink
|
||
|
|
url="http://java.sun.com/developer/technicalArticles/J2EE/jpa/index.html">Java
|
||
|
|
Persistence API</ulink> in a similar manner to the integration with
|
||
|
|
Hibernate or JDO, while being aware of the underlying implementation in
|
||
|
|
order to provide additional features.</para>
|
||
|
|
|
||
|
|
<section id="orm-jpa-setup">
|
||
|
|
<title>JPA setup in a Spring environment</title>
|
||
|
|
|
||
|
|
<para>Spring JPA offers three ways of setting up JPA
|
||
|
|
<interfacename>EntityManagerFactory</interfacename>:</para>
|
||
|
|
|
||
|
|
<section id="orm-jpa-setup-lemfb">
|
||
|
|
<title><classname>LocalEntityManagerFactoryBean</classname></title>
|
||
|
|
|
||
|
|
<para>The <classname>LocalEntityManagerFactoryBean</classname> creates
|
||
|
|
an <interfacename>EntityManagerFactory</interfacename> suitable for
|
||
|
|
environments which solely use JPA for data access. The factory bean
|
||
|
|
will use the JPA <interfacename>PersistenceProvider</interfacename>
|
||
|
|
autodetection mechanism (according to JPA's Java SE bootstrapping)
|
||
|
|
and, in most cases, requires only the persistence unit name to be
|
||
|
|
specified:</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<bean id="myEmf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
|
||
|
|
<property name="persistenceUnitName" value="myPersistenceUnit"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<para>This is the simplest but also most limited form of JPA
|
||
|
|
deployment. There is no way to link to an existing JDBC
|
||
|
|
<interfacename>DataSource</interfacename> and no support for global
|
||
|
|
transactions, for example. Furthermore, weaving (byte-code
|
||
|
|
transformation) of persistent classes is provider-specific, often
|
||
|
|
requiring a specific JVM agent to specified on startup. All in all,
|
||
|
|
this option is only really sufficient for standalone applications and
|
||
|
|
test environments (which is exactly what the JPA specification
|
||
|
|
designed it for).</para>
|
||
|
|
|
||
|
|
<para><emphasis>Only use this option in simple deployment environments
|
||
|
|
like standalone applications and integration tests.</emphasis></para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-jpa-setup-jndi">
|
||
|
|
<title><classname>Obtaining an EntityManagerFactory from
|
||
|
|
JNDI</classname></title>
|
||
|
|
|
||
|
|
<para>Obtaining an <interfacename>EntityManagerFactory</interfacename>
|
||
|
|
from JNDI (for example in a Java EE 5 environment), is just a matter
|
||
|
|
of changing the XML configuration:</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<jee:jndi-lookup id="myEmf" jndi-name="persistence/myPersistenceUnit"/>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<para>This assumes standard Java EE 5 bootstrapping, with the Java EE
|
||
|
|
server autodetecting persistence units (i.e.
|
||
|
|
<literal>META-INF/persistence.xml</literal> files in application jars)
|
||
|
|
and <literal>persistence-unit-ref</literal> entries in the Java EE
|
||
|
|
deployment descriptor (e.g. <literal>web.xml</literal>) defining
|
||
|
|
environment naming context locations for those persistence
|
||
|
|
units.</para>
|
||
|
|
|
||
|
|
<para>In such a scenario, the entire persistence unit deployment,
|
||
|
|
including the weaving (byte-code transformation) of persistent
|
||
|
|
classes, is up to the Java EE server. The JDBC
|
||
|
|
<interfacename>DataSource</interfacename> is defined through a JNDI
|
||
|
|
location in the <literal>META-INF/persistence.xml</literal> file;
|
||
|
|
EntityManager transactions are integrated with the server's JTA
|
||
|
|
subsystem. Spring merely uses the obtained
|
||
|
|
<interfacename>EntityManagerFactory</interfacename>, passing it on to
|
||
|
|
application objects via dependency injection, and managing
|
||
|
|
transactions for it (typically through
|
||
|
|
<classname>JtaTransactionManager</classname>).</para>
|
||
|
|
|
||
|
|
<para>Note that, in case of multiple persistence units used in the
|
||
|
|
same application, the bean names of such a JNDI-retrieved persistence
|
||
|
|
units should match the persistence unit names that the application
|
||
|
|
uses to refer to them (e.g. in <literal>@PersistenceUnit</literal> and
|
||
|
|
<literal>@PersistenceContext</literal> annotations).</para>
|
||
|
|
|
||
|
|
<para><emphasis>Use this option when deploying to a Java EE 5 server.
|
||
|
|
Check your server's documentation on how to deploy a custom JPA
|
||
|
|
provider into your server, allowing for a different provider than the
|
||
|
|
server's default. </emphasis></para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-jpa-setup-lcemfb">
|
||
|
|
<title><classname>LocalContainerEntityManagerFactoryBean</classname></title>
|
||
|
|
|
||
|
|
<para>The
|
||
|
|
<classname>LocalContainerEntityManagerFactoryBean</classname> gives
|
||
|
|
full control over <interfacename>EntityManagerFactory</interfacename>
|
||
|
|
configuration and is appropriate for environments where fine-grained
|
||
|
|
customization is required. The
|
||
|
|
<classname>LocalContainerEntityManagerFactoryBean</classname> will
|
||
|
|
create a <interfacename>PersistenceUnitInfo</interfacename> based on
|
||
|
|
the <literal>persistence.xml</literal> file, the supplied
|
||
|
|
<literal>dataSourceLookup</literal> strategy and the specified
|
||
|
|
<literal>loadTimeWeaver</literal>. It is thus possible to work with
|
||
|
|
custom DataSources outside of JNDI and to control the weaving
|
||
|
|
process.</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<bean id="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
|
||
|
|
<property name="dataSource" ref="someDataSource"/>
|
||
|
|
<property name="loadTimeWeaver">
|
||
|
|
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
|
||
|
|
</property>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<para>A typical <literal>persistence.xml</literal> file looks as follows:</para>
|
||
|
|
|
||
|
|
<programlisting><persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
|
||
|
|
|
||
|
|
<persistence-unit name="myUnit" transaction-type="RESOURCE_LOCAL">
|
||
|
|
<mapping-file>META-INF/orm.xml</mapping-file>
|
||
|
|
<exclude-unlisted-classes/>
|
||
|
|
</persistence-unit>
|
||
|
|
|
||
|
|
</persistence></programlisting>
|
||
|
|
|
||
|
|
<para><emphasis>NOTE: The "exclude-unlisted-classes" element always
|
||
|
|
indicates that NO scanning for annotated entity classes is supposed
|
||
|
|
to happen, in order to support the
|
||
|
|
<literal><exclude-unlisted-classes/></literal> shortcut.
|
||
|
|
This is in line with the JPA specification (which suggests that shortcut)
|
||
|
|
but unfortunately in conflict with the JPA XSD (which implies "false"
|
||
|
|
for that shortcut). As a consequence,
|
||
|
|
"<literal><exclude-unlisted-classes> false </exclude-unlisted-classes/></literal>"
|
||
|
|
is not supported! Simply omit the "exclude-unlisted-classes" element if
|
||
|
|
you would like entity class scanning to actually happen.</emphasis></para>
|
||
|
|
|
||
|
|
<para>This is the most powerful JPA setup option, allowing for
|
||
|
|
flexible local configuration within the application. It supports links
|
||
|
|
to an existing JDBC <interfacename>DataSource</interfacename>,
|
||
|
|
supports both local and global transactions, etc. However, it also
|
||
|
|
imposes requirements onto the runtime environment, such as the
|
||
|
|
availability of a weaving-capable ClassLoader if the persistence
|
||
|
|
provider demands byte-code transformation.</para>
|
||
|
|
|
||
|
|
<para>Note that this option may conflict with the built-in JPA
|
||
|
|
capabilities of a Java EE 5 server. So when running in a full Java EE
|
||
|
|
5 environment, consider obtaining your
|
||
|
|
<interfacename>EntityManagerFactory</interfacename> from JNDI.
|
||
|
|
Alternatively, specify a custom "persistenceXmlLocation" on your
|
||
|
|
<classname>LocalContainerEntityManagerFactoryBean</classname>
|
||
|
|
definition, e.g. "META-INF/my-persistence.xml", and only include a
|
||
|
|
descriptor with that name in your application jar files. Since the
|
||
|
|
Java EE 5 server will only look for default
|
||
|
|
<literal>META-INF/persistence.xml</literal> files, it will ignore such
|
||
|
|
custom persistence units and hence avoid conflicts with a
|
||
|
|
Spring-driven JPA setup upfront. (This applies to Resin 3.1, for
|
||
|
|
example.)</para>
|
||
|
|
|
||
|
|
<para><emphasis>Use this option for full JPA capabilities in a
|
||
|
|
Spring-based application environment. This includes web containers
|
||
|
|
such as Tomcat as well as standalone applications and integration
|
||
|
|
tests with sophisticated persistence requirements.</emphasis></para>
|
||
|
|
|
||
|
|
<sidebar>
|
||
|
|
<title>When is load-time weaving required?</title>
|
||
|
|
|
||
|
|
<para>Not all JPA providers impose the need of a JVM agent
|
||
|
|
(Hibernate being an example). If your provider does not require an
|
||
|
|
agent or you have other alternatives (for example applying
|
||
|
|
enhancements at build time through a custom compiler or an ant task)
|
||
|
|
the load-time weaver <emphasis role="bold">should not</emphasis> be
|
||
|
|
used.</para>
|
||
|
|
</sidebar>
|
||
|
|
|
||
|
|
<para>The <interfacename>LoadTimeWeaver</interfacename> interface is a
|
||
|
|
Spring-provided class that allows JPA
|
||
|
|
<interfacename>ClassTransformer</interfacename> instances to be
|
||
|
|
plugged in a specific manner depending on the environment (web
|
||
|
|
container/application server). Hooking
|
||
|
|
<literal>ClassTransformers</literal> through a Java 5 <ulink
|
||
|
|
url="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/instrument/package-summary.html">agent</ulink>
|
||
|
|
is typically not efficient - the agents work against the
|
||
|
|
<emphasis>entire virtual machine</emphasis> and inspect
|
||
|
|
<emphasis>every</emphasis> class that is loaded - something that is
|
||
|
|
typically undesirable in a production server enviroment.</para>
|
||
|
|
|
||
|
|
<para>Spring provides a number of
|
||
|
|
<interfacename>LoadTimeWeaver</interfacename> implementations for
|
||
|
|
various environments, allowing
|
||
|
|
<interfacename>ClassTransformer</interfacename> instances to be
|
||
|
|
applied only <emphasis>per ClassLoader</emphasis> and not per VM.</para>
|
||
|
|
|
||
|
|
<para>The following sections will discuss typical JPA weaving setup on
|
||
|
|
Tomcat as well as using Spring's VM agent. See the AOP chapter section
|
||
|
|
entitled <xref linkend="aop-aj-ltw-spring"/> for details on how to set
|
||
|
|
up general load-time weaving, covering Tomcat and the VM agent as well
|
||
|
|
as WebLogic, OC4J, GlassFish and Resin.</para>
|
||
|
|
|
||
|
|
<section id="orm-jpa-setup-lcemfb-tomcat">
|
||
|
|
<title>Tomcat load-time weaving setup (5.0+)</title>
|
||
|
|
|
||
|
|
<para><ulink url="http://tomcat.apache.org/">Apache
|
||
|
|
Tomcat's</ulink> default ClassLoader does not support class
|
||
|
|
transformation but allows custom ClassLoaders to be used. Spring
|
||
|
|
offers the <classname>TomcatInstrumentableClassLoader</classname>
|
||
|
|
(inside the
|
||
|
|
<literal>org.springframework.instrument.classloading.tomcat</literal>
|
||
|
|
package) which extends the Tomcat ClassLoader
|
||
|
|
(<classname>WebappClassLoader</classname>) and allows JPA
|
||
|
|
<classname>ClassTransformer</classname> instances to 'enhance' all
|
||
|
|
classes loaded by it. In short, JPA transformers will be applied
|
||
|
|
only inside a specific web application (which uses the
|
||
|
|
<classname>TomcatInstrumentableClassLoader</classname>).</para>
|
||
|
|
|
||
|
|
<para>In order to use the custom ClassLoader on:</para>
|
||
|
|
|
||
|
|
<itemizedlist>
|
||
|
|
<listitem>
|
||
|
|
<para>Tomcat 5.0.x/5.5.x</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<orderedlist>
|
||
|
|
<listitem>
|
||
|
|
<para>Copy <literal>spring-tomcat-weaver.jar</literal> into
|
||
|
|
<emphasis>$CATALINA_HOME</emphasis>/server/lib (where
|
||
|
|
<emphasis>$CATALINA_HOME</emphasis> represents the root of the
|
||
|
|
Tomcat installation).</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>Instruct Tomcat to use the custom ClassLoader (instead
|
||
|
|
of the default one) by editing the web application context
|
||
|
|
file:</para>
|
||
|
|
|
||
|
|
<programlisting><Context path="/myWebApp" docBase="/my/webApp/location">
|
||
|
|
<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
|
||
|
|
</Context></programlisting>
|
||
|
|
|
||
|
|
<para>Tomcat 5.0.x and 5.5.x series support several context
|
||
|
|
locations: server configuration file
|
||
|
|
(<emphasis>$CATALINA_HOME/conf/server.xml</emphasis>), the
|
||
|
|
default context configuration
|
||
|
|
(<emphasis>$CATALINA_HOME/conf/context.xml</emphasis>) that
|
||
|
|
affects all deployed web applications and per-webapp
|
||
|
|
configurations, deployed on the server
|
||
|
|
<emphasis>($CATALINA_HOME/conf/[enginename]/[hostname]/my-webapp-context.xml</emphasis>)
|
||
|
|
side or along with the webapp
|
||
|
|
(<emphasis>your-webapp.war/META-INF/context.xml</emphasis>).
|
||
|
|
For efficiency, inside the web-app configuration style is
|
||
|
|
recommended since only applications which use JPA will use the
|
||
|
|
custom ClassLoader. See the Tomcat 5.x <ulink
|
||
|
|
url="http://tomcat.apache.org/tomcat-5.5-doc/config/context.html">documentation</ulink>
|
||
|
|
for more details about available context locations.</para>
|
||
|
|
|
||
|
|
<para>Note that versions prior to 5.5.20 contained a bug in
|
||
|
|
the XML configuration parsing preventing usage of
|
||
|
|
<literal>Loader</literal> tag inside
|
||
|
|
<emphasis>server.xml</emphasis> (no matter if a ClassLoader is
|
||
|
|
specified or not (be it the official or a custom one). See
|
||
|
|
Tomcat's bugzilla for <ulink
|
||
|
|
url="http://issues.apache.org/bugzilla/show_bug.cgi?id=39704">more
|
||
|
|
details</ulink>.</para>
|
||
|
|
|
||
|
|
<para>If you are using Tomcat 5.5.20+ you can set
|
||
|
|
<emphasis>useSystemClassLoaderAsParent</emphasis> to
|
||
|
|
<literal>false</literal> to fix the problem: <programlisting><Context path="/myWebApp" docBase="/my/webApp/location">
|
||
|
|
<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"
|
||
|
|
useSystemClassLoaderAsParent="false"/>
|
||
|
|
</Context></programlisting></para>
|
||
|
|
</listitem>
|
||
|
|
</orderedlist>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>Tomcat 6.0.x</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<orderedlist>
|
||
|
|
<listitem>
|
||
|
|
<para>Copy <literal>spring-tomcat-weaver.jar</literal> into
|
||
|
|
<emphasis>$CATALINA_HOME</emphasis>/lib (where
|
||
|
|
<emphasis>$CATALINA_HOME</emphasis> represents the root of the
|
||
|
|
Tomcat installation).</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>Instruct Tomcat to use the custom ClassLoader (instead
|
||
|
|
of the default one) by editing the web application context
|
||
|
|
file:</para>
|
||
|
|
|
||
|
|
<programlisting><Context path="/myWebApp" docBase="/my/webApp/location">
|
||
|
|
<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
|
||
|
|
</Context></programlisting>
|
||
|
|
|
||
|
|
<para>Tomcat 6.0.x (similar to 5.0.x/5.5.x) series support
|
||
|
|
several context locations: server configuration file
|
||
|
|
(<emphasis>$CATALINA_HOME/conf/server.xml</emphasis>), the
|
||
|
|
default context configuration
|
||
|
|
(<emphasis>$CATALINA_HOME/conf/context.xml</emphasis>) that
|
||
|
|
affects all deployed web applications and per-webapp
|
||
|
|
configurations, deployed on the server
|
||
|
|
<emphasis>($CATALINA_HOME/conf/[enginename]/[hostname]/my-webapp-context.xml</emphasis>)
|
||
|
|
side or along with the webapp
|
||
|
|
(<emphasis>your-webapp.war/META-INF/context.xml</emphasis>).
|
||
|
|
For efficiency, inside the web-app configuration style is
|
||
|
|
recommended since only applications which use JPA will use the
|
||
|
|
custom ClassLoader. See the Tomcat 5.x <ulink
|
||
|
|
url="http://tomcat.apache.org/tomcat-6.0-doc/config/context.html">documentation</ulink>
|
||
|
|
for more details about available context locations.</para>
|
||
|
|
</listitem>
|
||
|
|
</orderedlist>
|
||
|
|
</itemizedlist>
|
||
|
|
|
||
|
|
<para>The last step required on all Tomcat versions, is to use the
|
||
|
|
appropriate the <interfacename>LoadTimeWeaver</interfacename> when
|
||
|
|
configuring
|
||
|
|
<classname>LocalContainerEntityManagerFactoryBean</classname>:</para>
|
||
|
|
|
||
|
|
<programlisting><bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
|
||
|
|
<property name="loadTimeWeaver">
|
||
|
|
<bean class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
|
||
|
|
</property>
|
||
|
|
</bean></programlisting>
|
||
|
|
|
||
|
|
<para>Using this technique, JPA applications relying on
|
||
|
|
instrumentation, can run in Tomcat without the need of an agent.
|
||
|
|
This is important especially when hosting applications which rely on
|
||
|
|
different JPA implementations since the JPA transformers are applied
|
||
|
|
only at ClassLoader level and thus, are isolated from each
|
||
|
|
other.</para>
|
||
|
|
|
||
|
|
<note>
|
||
|
|
<para>If TopLink is being used a JPA provider under Tomcat, please
|
||
|
|
place the toplink-essentials jar under
|
||
|
|
<emphasis>$CATALINA_HOME</emphasis>/shared/lib folder instead of
|
||
|
|
your war.</para>
|
||
|
|
</note>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-jpa-setup-lcemfb-agent">
|
||
|
|
<title>General load-time weaving using the VM agent</title>
|
||
|
|
|
||
|
|
<para>For environments where class instrumentation is required but
|
||
|
|
are not supported by the existing LoadTimeWeaver implementations, a
|
||
|
|
JDK agent can be the only solution. For such cases, Spring provides
|
||
|
|
<classname>InstrumentationLoadTimeWeaver</classname> which requires
|
||
|
|
a Spring-specific (but very general) VM agent (<filename
|
||
|
|
class="libraryfile">spring-agent.jar</filename>):</para>
|
||
|
|
|
||
|
|
<programlisting><bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
|
||
|
|
<property name="loadTimeWeaver">
|
||
|
|
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
|
||
|
|
</property>
|
||
|
|
</bean></programlisting>
|
||
|
|
|
||
|
|
<para>Note that the virtual machine has to be started with the
|
||
|
|
Spring agent, by supplying the following JVM options:</para>
|
||
|
|
|
||
|
|
<programlisting>-javaagent:/path/to/spring-agent.jar</programlisting>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-jpa-setup-lcemfb-weaver">
|
||
|
|
<title>Context-wide load-time weaver setup</title>
|
||
|
|
|
||
|
|
<para>Since Spring 2.5, a context-wide <interfacename>LoadTimeWeaver</interfacename>
|
||
|
|
can be configured using the <literal>context:load-time-weaver</literal> configuration
|
||
|
|
element. Such a 'global' weaver will be picked up by all JPA
|
||
|
|
<classname>LocalContainerEntityManagerFactoryBeans</classname> automatically.</para>
|
||
|
|
|
||
|
|
<para>This is the preferred way of setting up a load-time weaver, delivering
|
||
|
|
autodetection of the platform (WebLogic, OC4J, GlassFish, Tomcat, Resin, VM agent)
|
||
|
|
as well as automatic propagation of the weaver to all weaver-aware beans.</para>
|
||
|
|
|
||
|
|
<programlisting><context:load-time-weaver/>
|
||
|
|
|
||
|
|
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
|
||
|
|
...
|
||
|
|
</bean></programlisting>
|
||
|
|
|
||
|
|
<para>See the section entitled <xref linkend="aop-aj-ltw-spring"/>
|
||
|
|
for details on how to set up general load-time weaving, covering Tomcat
|
||
|
|
and the VM agent as well as WebLogic, OC4J, GlassFish and Resin.</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-jpa-multiple-pu">
|
||
|
|
<title>Dealing with multiple persistence units</title>
|
||
|
|
|
||
|
|
<para>For applications that rely on multiple persistence units
|
||
|
|
locations (stored in various jars in the classpath for example),
|
||
|
|
Spring offers the
|
||
|
|
<interfacename>PersistenceUnitManager</interfacename> to act as a
|
||
|
|
central repository and avoid the (potentially expensive) persistence
|
||
|
|
units discovery process. The default implementation allows multiple
|
||
|
|
locations to be specified (by default, the classpath is searched for
|
||
|
|
<filename>'META-INF/persistence.xml'</filename> files) which are
|
||
|
|
parsed and later on retrieved through the persistence unit
|
||
|
|
name:</para>
|
||
|
|
|
||
|
|
<programlisting><bean id="pum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
|
||
|
|
<property name="persistenceXmlLocation">
|
||
|
|
<list>
|
||
|
|
<value>org/springframework/orm/jpa/domain/persistence-multi.xml</value>
|
||
|
|
<value>classpath:/my/package/**/custom-persistence.xml</value>
|
||
|
|
<value>classpath*:META-INF/persistence.xml</value>
|
||
|
|
</list>
|
||
|
|
</property>
|
||
|
|
<property name="dataSources">
|
||
|
|
<map>
|
||
|
|
<entry key="localDataSource" value-ref="local-db"/>
|
||
|
|
<entry key="remoteDataSource" value-ref="remote-db"/>
|
||
|
|
</map>
|
||
|
|
</property>
|
||
|
|
<lineannotation><!-- if no datasource is specified, use this one --></lineannotation>
|
||
|
|
<property name="defaultDataSource" ref="remoteDataSource"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
|
||
|
|
<property name="persistenceUnitManager" ref="pum"/>
|
||
|
|
</bean></programlisting>
|
||
|
|
|
||
|
|
<para>Note that the default implementation allows customization of the
|
||
|
|
persistence unit infos before feeding them to the JPA provider
|
||
|
|
declaratively through its properties (which affect <emphasis>all</emphasis>
|
||
|
|
hosted units) or programmatically, through the
|
||
|
|
<interfacename>PersistenceUnitPostProcessor</interfacename>
|
||
|
|
(which allows persistence unit selection). If no
|
||
|
|
<interfacename>PersistenceUnitManager</interfacename> is
|
||
|
|
specified, one will be created and used internally by
|
||
|
|
<classname>LocalContainerEntityManagerFactoryBean</classname>.</para>
|
||
|
|
</section>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-jpa-template">
|
||
|
|
<title><classname>JpaTemplate</classname> and
|
||
|
|
<classname>JpaDaoSupport</classname></title>
|
||
|
|
|
||
|
|
<para>Each JPA-based DAO will then receive a
|
||
|
|
<interfacename>EntityManagerFactory</interfacename> via dependency
|
||
|
|
injection. Such a DAO can be coded against plain JPA and work with the
|
||
|
|
given <interfacename>EntityManagerFactory</interfacename> or through
|
||
|
|
Spring's <classname>JpaTemplate</classname>:</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<bean id="myProductDao" class="product.ProductDaoImpl">
|
||
|
|
<property name="entityManagerFactory" ref="myEmf"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<programlisting>public class JpaProductDao implements ProductDao {
|
||
|
|
|
||
|
|
private JpaTemplate jpaTemplate;
|
||
|
|
|
||
|
|
public void setEntityManagerFactory(EntityManagerFactory emf) {
|
||
|
|
this.jpaTemplate = new JpaTemplate(emf);
|
||
|
|
}
|
||
|
|
|
||
|
|
public Collection loadProductsByCategory(final String category) throws DataAccessException {
|
||
|
|
return (Collection) this.jpaTemplate.execute(new JpaCallback() {
|
||
|
|
public Object doInJpa(EntityManager em) throws PersistenceException {
|
||
|
|
Query query = em.createQuery("from Product as p where p.category = :category");
|
||
|
|
query.setParameter("category", category);
|
||
|
|
List result = query.getResultList();
|
||
|
|
<lineannotation>// do some further processing with the result list</lineannotation>
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>The <interfacename>JpaCallback</interfacename> implementation
|
||
|
|
allows any type of JPA data access. The
|
||
|
|
<classname>JpaTemplate</classname> will ensure that
|
||
|
|
<interfacename>EntityManager</interfacename>s are properly opened and
|
||
|
|
closed and automatically participate in transactions. Moreover, the
|
||
|
|
<classname>JpaTemplate</classname> properly handles exceptions, making
|
||
|
|
sure resources are cleaned up and the appropriate transactions rolled
|
||
|
|
back. The template instances are thread-safe and reusable and they can
|
||
|
|
be kept as instance variable of the enclosing class. Note that
|
||
|
|
<classname>JpaTemplate</classname> offers single-step actions such as
|
||
|
|
find, load, merge, etc along with alternative convenience methods that
|
||
|
|
can replace one line callback implementations.</para>
|
||
|
|
|
||
|
|
<para>Furthermore, Spring provides a convenient
|
||
|
|
<classname>JpaDaoSupport</classname> base class that provides the
|
||
|
|
<literal>get/setEntityManagerFactory</literal> and
|
||
|
|
<methodname>getJpaTemplate()</methodname> to be used by
|
||
|
|
subclasses:</para>
|
||
|
|
|
||
|
|
<programlisting>public class ProductDaoImpl extends JpaDaoSupport implements ProductDao {
|
||
|
|
|
||
|
|
public Collection loadProductsByCategory(String category) throws DataAccessException {
|
||
|
|
Map<String, String> params = new HashMap<String, String>();
|
||
|
|
params.put("category", category);
|
||
|
|
return getJpaTemplate().findByNamedParams("from Product as p where p.category = :category", params);
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>Besides working with Spring's <classname>JpaTemplate</classname>,
|
||
|
|
one can also code Spring-based DAOs against the JPA, doing one's own
|
||
|
|
explicit <interfacename>EntityManager</interfacename> handling. As also
|
||
|
|
elaborated in the corresponding Hibernate section, the main advantage of
|
||
|
|
this approach is that your data access code is able to throw checked
|
||
|
|
exceptions. <classname>JpaDaoSupport</classname> offers a variety of
|
||
|
|
support methods for this scenario, for retrieving and releasing a
|
||
|
|
transaction <interfacename>EntityManager</interfacename>, as well as for
|
||
|
|
converting exceptions.</para>
|
||
|
|
|
||
|
|
<para><emphasis>JpaTemplate mainly exists as a sibling of JdoTemplate
|
||
|
|
and HibernateTemplate, offering the same style for people used to it.
|
||
|
|
For newly started projects, consider adopting the native JPA style of
|
||
|
|
coding data access objects instead, based on a "shared EntityManager"
|
||
|
|
reference obtained through the JPA
|
||
|
|
<literal>@PersistenceContext</literal> annotation (using Spring's
|
||
|
|
<classname>PersistenceAnnotationBeanPostProcessor</classname>; see below
|
||
|
|
for details.)</emphasis></para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-jpa-straight">
|
||
|
|
<title>Implementing DAOs based on plain JPA</title>
|
||
|
|
|
||
|
|
<note>
|
||
|
|
<para>While <interfacename>EntityManagerFactory</interfacename>
|
||
|
|
instances are thread-safe,
|
||
|
|
<interfacename>EntityManager</interfacename> instances are not. The
|
||
|
|
injected JPA <interfacename>EntityManager</interfacename> behave just
|
||
|
|
like an <interfacename>EntityManager</interfacename> fetched from an
|
||
|
|
application server's JNDI environment, as defined by the JPA
|
||
|
|
specification. It will delegate all calls to the current transactional
|
||
|
|
<interfacename>EntityManager</interfacename>, if any; else, it will
|
||
|
|
fall back to a newly created
|
||
|
|
<interfacename>EntityManager</interfacename> per operation, making it
|
||
|
|
thread-safe.</para>
|
||
|
|
</note>
|
||
|
|
|
||
|
|
<para>It is possible to write code against the plain JPA without using
|
||
|
|
any Spring dependencies, using an injected
|
||
|
|
<interfacename>EntityManagerFactory</interfacename> or
|
||
|
|
<interfacename>EntityManager</interfacename>. Note that Spring can
|
||
|
|
understand <interfacename>@PersistenceUnit</interfacename> and
|
||
|
|
<interfacename>@PersistenceContext</interfacename> annotations both at
|
||
|
|
field and method level if a
|
||
|
|
<classname>PersistenceAnnotationBeanPostProcessor</classname> is
|
||
|
|
enabled. A corresponding DAO implementation might look like this:</para>
|
||
|
|
|
||
|
|
<programlisting>public class ProductDaoImpl implements ProductDao {
|
||
|
|
|
||
|
|
private EntityManagerFactory emf;
|
||
|
|
|
||
|
|
@PersistenceUnit
|
||
|
|
public void setEntityManagerFactory(EntityManagerFactory emf) {
|
||
|
|
this.emf = emf;
|
||
|
|
}
|
||
|
|
|
||
|
|
public Collection loadProductsByCategory(String category) {
|
||
|
|
EntityManager em = this.emf.createEntityManager();
|
||
|
|
try {
|
||
|
|
Query query = em.createQuery("from Product as p where p.category = ?1");
|
||
|
|
query.setParameter(1, category);
|
||
|
|
return query.getResultList();
|
||
|
|
}
|
||
|
|
finally {
|
||
|
|
if (em != null) {
|
||
|
|
em.close();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>The DAO above has no dependency on Spring and still fits nicely
|
||
|
|
into a Spring application context, just like it would if coded against
|
||
|
|
Spring's <classname>JpaTemplate</classname>. Moreover, the DAO takes
|
||
|
|
advantage of annotations to require the injection of the default
|
||
|
|
<interfacename>EntityManagerFactory</interfacename>:</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<lineannotation><!-- bean post-processor for JPA annotations --></lineannotation>
|
||
|
|
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
|
||
|
|
|
||
|
|
<bean id="myProductDao" class="product.ProductDaoImpl"/>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<para>Note: As alternative to defining a
|
||
|
|
<classname>PersistenceAnnotationBeanPostProcessor</classname>
|
||
|
|
explicitly, consider using Spring 2.5's
|
||
|
|
<literal>context:annotation-config</literal> XML element in your
|
||
|
|
application context configuration. This will automatically register all
|
||
|
|
of Spring's standard post-processors for annotation-based configuration
|
||
|
|
(including <classname>CommonAnnotationBeanPostProcessor</classname>
|
||
|
|
etc).</para>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<lineannotation><!-- post-processors for all standard config annotations --></lineannotation>
|
||
|
|
<context:annotation-config/>
|
||
|
|
|
||
|
|
<bean id="myProductDao" class="product.ProductDaoImpl"/>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<para>The main issue with such a DAO is that it always creates a new
|
||
|
|
<interfacename>EntityManager</interfacename> via the factory. This can
|
||
|
|
be easily overcome by requesting a transactional
|
||
|
|
<interfacename>EntityManager</interfacename> (also called "shared
|
||
|
|
EntityManager", since it is a shared, thread-safe proxy for the actual
|
||
|
|
transactional EntityManager) to be injected instead of the
|
||
|
|
factory:</para>
|
||
|
|
|
||
|
|
<programlisting>public class ProductDaoImpl implements ProductDao {
|
||
|
|
|
||
|
|
@PersistenceContext
|
||
|
|
private EntityManager em;
|
||
|
|
|
||
|
|
public Collection loadProductsByCategory(String category) {
|
||
|
|
Query query = em.createQuery("from Product as p where p.category = :category");
|
||
|
|
query.setParameter("category", category);
|
||
|
|
return query.getResultList();
|
||
|
|
}
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<para>Note that the <literal>@PersistenceContext</literal> annotation
|
||
|
|
has an optional attribute <literal>type</literal>, which defaults to
|
||
|
|
<literal>PersistenceContextType.TRANSACTION</literal>. This default is
|
||
|
|
what you need to receive a "shared EntityManager" proxy. The
|
||
|
|
alternative, <literal>PersistenceContextType.EXTENDED</literal>, is a
|
||
|
|
completely different affair: This results in a so-called "extended
|
||
|
|
EntityManager", which is <emphasis>not thread-safe</emphasis> and hence
|
||
|
|
must not be used in a concurrently accessed component such as a
|
||
|
|
Spring-managed singleton bean. Extended EntityManagers are only supposed
|
||
|
|
to be used in stateful components that, for example, reside in a
|
||
|
|
session, with the lifecycle of the EntityManager not tied to a current
|
||
|
|
transaction but rather being completely up to the application.</para>
|
||
|
|
|
||
|
|
<sidebar>
|
||
|
|
<title>Method and Field level Injection</title>
|
||
|
|
|
||
|
|
<para>Annotations that indicate dependency injections (such as
|
||
|
|
<literal>@PersistenceUnit</literal> and
|
||
|
|
<literal>@PersistenceContext</literal>) can be applied on field or
|
||
|
|
methods inside a class, therefore the expression "method/field level
|
||
|
|
injection". Field-level annotations concise and easier to use while
|
||
|
|
method-level allow for processing the injected dependency. In both
|
||
|
|
cases the member visibility (public, protected, private) does not
|
||
|
|
matter.</para>
|
||
|
|
|
||
|
|
<para>What about class level annotations?</para>
|
||
|
|
|
||
|
|
<para>On the Java EE 5 platform, they are used for dependency declaration
|
||
|
|
and not for resource injection.</para>
|
||
|
|
</sidebar>
|
||
|
|
|
||
|
|
<para>The injected <interfacename>EntityManager</interfacename> is
|
||
|
|
Spring-managed (aware of the ongoing transaction). It is important to
|
||
|
|
note that even though the new implementation prefers method level
|
||
|
|
injection (of an <interfacename>EntityManager</interfacename> instead of
|
||
|
|
an <interfacename>EntityManagerFactory)</interfacename>, no change is
|
||
|
|
required in the application context XML due to annotation usage.</para>
|
||
|
|
|
||
|
|
<para>The main advantage of this DAO style is that it depends on Java
|
||
|
|
Persistence API; no import of any Spring class is required. Moreover, as
|
||
|
|
the JPA annotations are understood, the injections are applied
|
||
|
|
automatically by the Spring container. This is of course appealing from
|
||
|
|
a non-invasiveness perspective, and might feel more natural to JPA
|
||
|
|
developers.</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-jpa-exceptions">
|
||
|
|
<title>Exception Translation</title>
|
||
|
|
|
||
|
|
<para>However, the DAO throws the plain
|
||
|
|
<classname>PersistenceException</classname> exception class (which is
|
||
|
|
unchecked, and so does not have to be declared or caught) but also
|
||
|
|
<classname>IllegalArgumentException</classname> and
|
||
|
|
<classname>IllegalStateException</classname>, which means that callers
|
||
|
|
can only treat exceptions as generally fatal - unless they want to
|
||
|
|
depend on JPA'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 JPA-based and/or do not need any
|
||
|
|
special exception treatment. However, Spring offers a solution allowing
|
||
|
|
exception translation to be applied transparently through the
|
||
|
|
<interfacename>@Repository</interfacename> annotation:</para>
|
||
|
|
|
||
|
|
<programlisting>@Repository
|
||
|
|
public class ProductDaoImpl implements ProductDao {
|
||
|
|
|
||
|
|
<lineannotation>// class body here...</lineannotation>
|
||
|
|
|
||
|
|
}</programlisting>
|
||
|
|
|
||
|
|
<programlisting><beans>
|
||
|
|
|
||
|
|
<lineannotation><!-- <classname>Exception</classname> translation bean post processor --></lineannotation>
|
||
|
|
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
|
||
|
|
|
||
|
|
<bean id="myProductDao" class="product.ProductDaoImpl"/>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<para>The postprocessor will automatically look for all exception
|
||
|
|
translators (implementations of the
|
||
|
|
<interfacename>PersistenceExceptionTranslator</interfacename> interface)
|
||
|
|
and advise all beans marked with the
|
||
|
|
<interfacename>@Repository</interfacename> annotation so that the
|
||
|
|
discovered translators can intercept and apply the appropriate
|
||
|
|
translation on the thrown exceptions.</para>
|
||
|
|
|
||
|
|
<para>In summary: DAOs can be implemented based on the plain Java
|
||
|
|
Persistence API and annotations, while still being able to benefit from
|
||
|
|
Spring-managed transactions, dependency injection, and transparent
|
||
|
|
exception conversion (if desired) to Spring's custom exception
|
||
|
|
hierarchies.</para>
|
||
|
|
</section>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-jpa-tx">
|
||
|
|
<title>Transaction Management</title>
|
||
|
|
|
||
|
|
<para>To execute service operations within transactions, you can use
|
||
|
|
Spring's common declarative transaction facilities. For example:</para>
|
||
|
|
|
||
|
|
<programlisting><?xml version="1.0" encoding="UTF-8"?>
|
||
|
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
|
|
xmlns:aop="http://www.springframework.org/schema/aop"
|
||
|
|
xmlns:tx="http://www.springframework.org/schema/tx"
|
||
|
|
xsi:schemaLocation="
|
||
|
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
|
||
|
|
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
|
||
|
|
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
|
||
|
|
|
||
|
|
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
|
||
|
|
<property name="entityManagerFactory" ref="myEmf"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
<bean id="myProductService" class="product.ProductServiceImpl">
|
||
|
|
<property name="productDao" ref="myProductDao"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
<aop:config>
|
||
|
|
<aop:pointcut id="productServiceMethods" expression="execution(* product.ProductService.*(..))"/>
|
||
|
|
<aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/>
|
||
|
|
</aop:config>
|
||
|
|
|
||
|
|
<tx:advice id="txAdvice" transaction-manager="myTxManager">
|
||
|
|
<tx:attributes>
|
||
|
|
<tx:method name="increasePrice*" propagation="REQUIRED"/>
|
||
|
|
<tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/>
|
||
|
|
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
|
||
|
|
</tx:attributes>
|
||
|
|
</tx:advice>
|
||
|
|
|
||
|
|
</beans></programlisting>
|
||
|
|
|
||
|
|
<para>Spring JPA allows a configured
|
||
|
|
<classname>JpaTransactionManager</classname> to expose a JPA transaction
|
||
|
|
to JDBC access code that accesses the same JDBC
|
||
|
|
<interfacename>DataSource</interfacename>, provided that the registered
|
||
|
|
<interfacename>JpaDialect</interfacename> supports retrieval of the
|
||
|
|
underlying JDBC <interfacename>Connection</interfacename>. Out of the box,
|
||
|
|
Spring provides dialects for the Toplink, Hibernate and OpenJPA JPA
|
||
|
|
implementations. See the next section for details on the
|
||
|
|
<interfacename>JpaDialect</interfacename> mechanism.</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="orm-jpa-dialect">
|
||
|
|
<title><interfacename>JpaDialect</interfacename></title>
|
||
|
|
|
||
|
|
<para>As an advanced feature <classname>JpaTemplate</classname>,
|
||
|
|
<classname>JpaTransactionManager</classname> and subclasses of
|
||
|
|
<classname>AbstractEntityManagerFactoryBean</classname> support a custom
|
||
|
|
<interfacename>JpaDialect</interfacename>, to be passed into the
|
||
|
|
"jpaDialect" bean property. In such a scenario, the DAOs won't receive an
|
||
|
|
<interfacename>EntityManagerFactory</interfacename> reference but rather a
|
||
|
|
full <classname>JpaTemplate</classname> instance instead (for example,
|
||
|
|
passed into <classname>JpaDaoSupport</classname>'s "jpaTemplate"
|
||
|
|
property). A <interfacename>JpaDialect</interfacename> implementation can
|
||
|
|
enable some advanced features supported by Spring, usually in a
|
||
|
|
vendor-specific manner:</para>
|
||
|
|
|
||
|
|
<itemizedlist>
|
||
|
|
<listitem>
|
||
|
|
<para>applying specific transaction semantics (such as custom
|
||
|
|
isolation level or transaction timeout)</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>retrieving the transactional JDBC
|
||
|
|
<interfacename>Connection</interfacename> (for exposure to JDBC-based
|
||
|
|
DAOs)</para>
|
||
|
|
</listitem>
|
||
|
|
|
||
|
|
<listitem>
|
||
|
|
<para>advanced translation of <literal>PersistenceExceptions</literal>
|
||
|
|
to Spring <literal>DataAccessExceptions</literal></para>
|
||
|
|
</listitem>
|
||
|
|
</itemizedlist>
|
||
|
|
|
||
|
|
<para>This is particularly valuable for special transaction semantics and
|
||
|
|
for advanced translation of exception. Note that the default
|
||
|
|
implementation used (<classname>DefaultJpaDialect</classname>) doesn't
|
||
|
|
provide any special capabilities and if the above features are required,
|
||
|
|
the appropriate dialect has to be specified.</para>
|
||
|
|
|
||
|
|
<para>See the <interfacename>JpaDialect</interfacename> Javadoc for more
|
||
|
|
details of its operations and how they are used within Spring's JPA
|
||
|
|
support.</para>
|
||
|
|
</section>
|
||
|
|
</chapter>
|