|
|
O/R mapping integration
Of course often you want to use O/R mapping, rather than use relational data
access. Your overall application framework must support this also. Thus Spring
integrates out of the box with Hibernate (versions 2 and 3), JDO (versions 1
and 2), TopLink and other ORM products. Its data access architecture allows it
to integrate with any underlying data access technology. Spring and
Hibernate are a particularly popular combination.
Why would you use an ORM product plus Spring, instead of the ORM product
directly? Spring adds significant value in the following areas:
-
Session management. Spring offers efficient, easy, and safe handling
of units of work such as Hibernate or TopLink Sessions. Related code using
the ORM tool alone generally needs to use the same "Session" object for
efficiency and proper transaction handling. Spring can transparently create
and bind a session to the current thread, using either a declarative, AOP
method interceptor approach, or by using an explicit, "template" wrapper
class at the Java code level. Thus Spring solves many of the usage issues
that affect many users of ORM technology.
-
Resource management. Spring application contexts can handle the
location and configuration of Hibernate SessionFactories, JDBC datasources,
and other related resources. This makes these values easy to manage and
change.
-
Integrated transaction management. Spring allows you to wrap your ORM
code with either a declarative, AOP 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 we discuss later, you also
get the benefit of being able to use and swap various transaction managers,
without your ORM-related code being affected. As an added benefit,
JDBC-related code can fully integrate transactionally with ORM code, in the
case of most supported ORM tools. This is useful for handling functionality
not amenable to ORM.
-
Exception wrapping, as described above. Spring can wrap exceptions
from the ORM layer, converting them from proprietary (possibly checked)
exceptions, to a set of abstracted runtime exceptions. 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.
-
To avoid vendor lock-in. ORM solutions have different performance
other characterics, and there is no perfect one size fits all solution.
Alternatively, you may find that certain functionality is just not suited to
an implemention using your ORM tool. Thus it makes sense to decouple your
architecture from the tool-specific implementations of your data access
object interfaces. If you may ever need to switch to another implementation
for reasons of functionality, performance, or any other concerns, using
Spring now can make the eventual switch much easier. Spring's abstraction of
your ORM tool's Transactions and Exceptions, along with its IoC approach
which allow you to easily swap in mapper/DAO objects implementing
data-access functionality, make it easy to isolate all ORM-specific code in
one area of your application, without sacrificing any of the power of your
ORM tool. The PetClinic sample application shipped with Spring demonstrates
the portability benefits that Spring offers, through providing variants that
use JDBC, Hibernate, TopLink and Apache OJB to implement the persistence
layer.
-
Ease of testing. Spring's inversion of control approach makes it easy
to swap the implementations and locations of resources such as Hibernate
session factories, datasources, transaction managers, and mapper object
implementations (if needed). This makes it much easier to isolate and test
each piece of persistence-related code in isolation.
Above all, Spring facilitates a mix-and-match approach to data access. Despite
the claims of some ORM vendors, ORM is not the solution to all
problems, although it is a valuable productivity win in many cases. Spring
enables a consistent architecture, and transaction strategy, even if you mix
and match persistence approaches, even without using JTA.
In cases where ORM is not ideally suited, Spring's simplified JDBC is not the
only option: the "mapped statement" approach provided by
iBATIS SQL Maps is worth
a look. It provides a high level of control over SQL, while still automating
the creation of mapped objects from query results. Spring integrates with SQL
Maps out of the box. Spring's PetStore sample application illustrates iBATIS
integration. Transaction management
Abstracting a data access API is not enough; we also need to consider
transaction management. JTA is the obvious solution, but it's a cumbersome API
to use directly, and as a result many J2EE developers used to feel that EJB
CMT is the only rational option for transaction management. Spring has changed
that.
Spring provides its own abstraction for transaction management. Spring uses
this to deliver:
-
Programmatic transaction management via a callback template analogous to the
JdbcTemplate, which is much easier to use than straight JTA
-
Declarative transaction management analogous to EJB CMT, but without the
need for an EJB container. Actually, as we'll see, Spring's declarative
transaction management capability is a semantically compatible superset of
EJB CMT, with some unique and important benefits.
Spring's transaction abstraction is unique in that it's not tied to JTA or any
other transaction management technology. Spring uses the concept of a
transaction strategy that decouples application code from the
underlying transaction infrastructure (such as JDBC).
Why should you care about this? Isn't JTA the best answer for all transaction
management? If you're writing an application that uses only a single database,
you don't need the complexity of JTA. You're not interested in XA transactions
or two phase commit. You may not even need a high-end application server that
provides these things. But, on the other hand, you don't want to have to
rewrite your code should you ever have to work with multiple data sources.
Imagine you decide to avoid the overhead of JTA by using JDBC or Hibernate
transactions directly. If you ever need to work with multiple data sources,
you'll have to rip out all that transaction management code and replace it
with JTA transactions. This isn't very attractive and led most writers on
J2EE, including myself, to recommend using global JTA transactions
exclusively, effectively ruling out using a simple web container such as
Tomcat for transactional applications. Using the Spring transaction
abstraction, however, you only have to reconfigure Spring to use a JTA, rather
than JDBC or Hibernate, transaction strategy and you're done. This is a
configuration change, not a code change. Thus, Spring enables you to write
applications that can scale down as well as up.
|