2009-03-19 04:00:49 +08:00
<?xml version="1.0" encoding="UTF-8"?>
2009-04-30 12:16:07 +08:00
< !DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
2009-04-15 05:37:40 +08:00
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
2009-03-19 04:00:49 +08:00
<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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< bean id="myProductDao" class="product.ProductDaoImpl">
< property name="sessionFactory" ref="mySessionFactory"/>
< /bean>
< /beans> </programlisting>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class ProductDaoImpl implements ProductDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class ProductDaoImpl implements ProductDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class HibernateProductDao extends HibernateDaoSupport implements ProductDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class ProductDaoImpl implements ProductDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class ProductServiceImpl implements ProductService {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class ProductServiceImpl implements ProductService {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < ?xml version="1.0" encoding="UTF-8"?>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< bean id="myProductDao" class="product.ProductDaoImpl">
< property name="persistenceManagerFactory" ref="myPmf"/>
< /bean>
< /beans> </programlisting>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class ProductDaoImpl implements ProductDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class ProductDaoImpl extends JdoDaoSupport implements ProductDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class ProductDaoImpl implements ProductDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class ProductDaoImpl implements ProductDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < ?xml version="1.0" encoding="UTF-8"?>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < toplink-configuration>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< bean id="myProductDao" class="product.ProductDaoImpl">
< property name="sessionFactory" ref="mySessionFactory"/>
< /bean>
< /beans> </programlisting>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class TopLinkProductDao implements ProductDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class ProductDaoImpl extends TopLinkDaoSupport implements ProductDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class ProductDaoImpl implements ProductDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < ?xml version="1.0" encoding="UTF-8"?>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < sqlMap namespace="Account">
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < sqlMapConfig>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class SqlMapAccountDao extends SqlMapClientDaoSupport implements AccountDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class SqlMapAccountDao extends SqlMapClientDaoSupport implements AccountDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class SqlMapAccountDao implements AccountDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < Context path="/myWebApp" docBase="/my/webApp/location">
2009-03-19 04:00:49 +08:00
< 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
2009-04-13 21:12:38 +08:00
<literal > false</literal> to fix the problem: <programlisting language= "xml" > < Context path="/myWebApp" docBase="/my/webApp/location">
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < Context path="/myWebApp" docBase="/my/webApp/location">
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < context:load-time-weaver/>
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < bean id="pum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
2009-03-19 04:00:49 +08:00
< 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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
< bean id="myProductDao" class="product.ProductDaoImpl">
< property name="entityManagerFactory" ref="myEmf"/>
< /bean>
< /beans> </programlisting>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class JpaProductDao implements ProductDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class ProductDaoImpl extends JpaDaoSupport implements ProductDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class ProductDaoImpl implements ProductDao {
2009-03-19 04:00:49 +08:00
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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
<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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
<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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > public class ProductDaoImpl implements ProductDao {
2009-03-19 04:00:49 +08:00
@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>
2009-04-13 21:12:38 +08:00
<programlisting language= "java" > @Repository
2009-03-19 04:00:49 +08:00
public class ProductDaoImpl implements ProductDao {
<lineannotation > // class body here...</lineannotation>
}</programlisting>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < beans>
2009-03-19 04:00:49 +08:00
<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>
2009-04-13 21:12:38 +08:00
<programlisting language= "xml" > < ?xml version="1.0" encoding="UTF-8"?>
2009-03-19 04:00:49 +08:00
< 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>