Chapter 1. Architecting Your Application with Spring, Hibernate, and Patterns

Persistence is typically the lifeblood of an application, providing the long-term memory that software requires in order to be useful across multiple invocations. Despite its importance, the architecture of a persistence tier is rarely granted adequate consideration during the design or implementation stages of an application. The consequences of this lack of planning can be far-reaching and devastating to an organization.

The primary goal of this book is to provide you with the best practices, tools, and strategies required to architect and implement a solid and effective persistence tier. Many of the concepts found on these pages were gleaned from real-world, practical experience designing and building web applications intended to scale to millions of daily users. Our objective is to illustrate the patterns and approaches that have worked for us, while examining the integration details for using Spring and Hibernate in your own applications.

One important lesson we've acquired over the years is that it's often best to learn by example. To this end, we will be building a real-world application over the course of the book: an Image Gallery web application, which allows users to view slideshows and exhibitions curated by administrators. To emphasize proven, pragmatic solutions and architectural patterns for building scalable and maintainable applications, each chapter will focus on a different aspect of application development, in regards to persistence. Through illustrated code samples and discussion, we will trace the design, architecture, and implementation of a real working application. Starting with the foundation, each successive chapter will build upon the previous one, adding new layers, features, and tests. And of course, as with any real-world application, we will do significant refactoring as we discover new capabilities of Spring and Hibernate, as well as alternative strategies and frameworks. In fact, the last two chapters will re-architect our Image Gallery application entirely, as we examine two new frameworks founded on the concept of "convention over configuration." Intended for a more rapid style of development, Grails and Roo offer a more holistic and consistent development environment with powerful features popularized by frameworks from dynamic languages, such as Ruby on Rails and Django.

The Benefit of a Consistent Approach

As you will learn throughout this book, the manner in which data is saved and queried is an integral part of every application. In fact, the persistence layer often serves as the foundation upon which an application is built. Building on top of this foundation are the three core components of a standard Spring-based persistence tier: the domain model, the Data Access Object layer, and the service facade. Don't worry if some of these terms are unfamiliar to you. In the following chapters, we will explain the purpose and function of each of these components, demonstrating the role each plays in an application.

While we don't suggest that there is only one correct approach to architecting an application, we do want to emphasize the benefit of using key design patterns and best practices. This is a theme that you will see cropping up over and over again.

The Significance of Dependency Injection

The Spring Framework has helped to take much of the guesswork out of designing and building an application. It has become the de facto standard for integrating disparate components and frameworks, and has evolved far beyond its dependency injection roots. The purpose of dependency injection is to decouple the work of resolving external software components from your application business logic. Without dependency injection, the details of how a component accesses required services can get muddled in with the component's code. This not only increases the potential for errors, adds code bloat, and magnifies maintenance complexities; it couples components together more closely, making it difficult to modify dependencies when refactoring or testing.

By its very nature, Spring helps to enforce best coding practices and reduce dependency on external frameworks, or even classes within an application. At the simplest level, Spring is a lightweight IoC container, meaning that it will assume the responsibility of wiring your application dependencies. Exactly how this wiring responsibility is handled will be discussed in depth throughout this book. However, a theme you will see replayed throughout these pages is how Spring effortlessly ties components together in a loosely coupled manner. This has far-reaching effects for any application, as it allows code to be more easily refactored and maintained. And in the context of this book, it allows developers to build a persistence tier that is not directly tied to a particular implementation or framework.

Spring owes much of its success to the sheer number of integration points it provides, covering a wide range of frameworks and technologies. As developers realized the benefits gleaned from using Spring for integrating the various components within their own code, many new abstractions appeared that relied on Spring to integrate popular open source frameworks. Using Spring to integrate a particular framework not only simplifies the introduction of the framework, it allows the integration to be performed in a consistent manner—no different than the way collaborating components are wired within the context of an application. Additionally, using Spring's dependency injection to wire in a key framework ensures the integration is done in a decoupled way.

One of the leading catalysts for Spring's adoption was its support for the open source, object-relational mapping (ORM) framework, Hibernate. As the Spring Framework began to grow in popularity, the Java development community was also buzzing about Hibernate. It was a pivotal time for open source frameworks, as both Spring and Hibernate offered revolutionary solutions that would change the way many new applications were architected and implemented. As you will see, Spring and Hibernate complement each other in numerous ways, and each is partially responsible for the other's success and widespread adoption.

A Synergistic Partnership

In this book, we will focus on showing how Spring and Hibernate can be used together most effectively. Nevertheless, we will still emphasize strategies for decoupling Hibernate from your application. This is not because we have any concerns about using Hibernate, but because loose coupling provides a cleaner separation of concerns.

No matter how good a framework might be, it's always better to keep dependencies decoupled. Not only does an agnostic persistence tier lead to better, cleaner, more maintainable code (as well as portability from one persistence technology to another), but it also ensures consistency across your application. Suddenly, your code is supported by a backbone that handles dependency wiring, provides aspect-oriented programming (AOP) capability, and generates cohesive configuration metadata that implicitly documents the way your application's pieces fit together.

Spring encourages design practices that help to keep all of your application's dependencies decoupled. Whether it be an external framework, an application component, or even Spring or Hibernate themselves, ensuring that collaborating components are not directly tied together helps prevent the concerns of one layer from leaking into another. By delegating all your wiring details to Spring, you not only simplify your code base by relieving the need to create infrastructural "access code," you also ensure that components are kept distinct. In the next few chapters, you will learn how coding to interfaces and using Spring's ORM abstractions and generic exception hierarchy can help to achieve these goals.

The Story of Spring's and Hibernate's Success

The rise in Spring's popularity stems from more than just its ability to reduce code complexity by helping to wire dependencies together. Much of the early excitement around the Spring Framework was due to its support for other leading open source frameworks, including Hibernate. Hibernate was one of the first open source ORM frameworks that provided an enterprise-level solution for building a persistence tier. Spring's ability to externalize integration details to an XML configuration file or express dependency injection through Java annotations provided a powerful abstraction that greatly simplified and standardized the integration efforts required to bootstrap Hibernate into an application.

ORM frameworks provide an abstraction layer over the actual persistence technology being used (usually a relational database), allowing developers to focus on the object-oriented details of their domain model, rather than lower-level database concerns. There is an inherent impedance mismatch between the relational-table world of databases and the object-oriented world of Java, making an effective ORM abstraction difficult to implement. This impedance mismatch is due to the fundamental differences between relational databases and object-oriented languages, such as Java. For example, relational databases don't implement core object-oriented principles such as polymorphism, encapsulation, and accessibility. Furthermore, the notion of equality is vastly different between Java and SQL. We will discuss some of these differences throughout this book, examining approaches to bridging the gap between a SQL database and a Java domain model.

Hibernate represented a significant step in bridging this gap by offering a powerful open source framework for expressing an object-oriented domain model, and defining the ways in which the tables and columns of a database synchronized with the object instances and properties in JavaBeans.

A Better Approach for Integration

Despite the improvements and efficiency with which a persistence tier could now be developed, integrating Hibernate into an application could still be a painstaking endeavor. With no standardized integration approach, developers were left to continuously reinvent the wheel, spending significant resources on the development and maintenance of the infrastructure code required to wedge Hibernate into their applications.

As Hibernate grew in popularity, the Spring Framework started to gain momentum as well. When Spring first came on the scene, its mission was to make the development of server-side Java applications simpler. First and foremost, it offered a better solution to wiring application dependencies together. For this reason, Spring is often referred to as a container, meaning that it offers a centralized abstraction for integrating collaborating dependencies via configuration, rather than writing (often repetitive) code to handle this task.

Part of Spring's momentum stems from the way it enables applications to deliver enterprise-level features, such as declarative transactions and security, without requiring the overhead and complexity of an Enterprise JavaBean (EJB) container or forcing developers to grapple with the details of specific technologies or standards. Time has proven EJB, although powerful in theory, to be a victim of overengineering. Spring and Hibernate owe much of their success to the fact that they provide a more reasonable and effective solution than the EJB standard. While Spring offers a simpler approach to declarative transaction management, Hibernate provides a more robust and intuitive ORM abstraction. Both frameworks were built and popularized by the growing need for a solution that was less complex than previous offerings. With the success of Spring and Hibernate came a stronger emphasis on building applications that were simpler and lighter weight, significantly increasing both ease of maintenance and scalability.

Although dependency injection was Spring's core purpose, the framework has evolved far beyond its original IoC foundation. The Spring Framework has expanded into other areas that naturally blend with its IoC roots. Spring now provides a pluggable transactional management layer, AOP support, integration points with persistence frameworks (such as Hibernate), and a flexible web framework, called Spring MVC. The addition of these features was a gradual process, spurred by demand and necessity.

As Hibernate's popularity surged, developers began to rely on Spring's persistence abstractions to simplify the often daunting task of integrating Hibernate into an application. Thanks to Spring, the process of getting up and running with Hibernate became a great deal easier. Developers could start with a Spring configuration file that not only bootstrapped a Hibernate SessionFactory (allowing configuration details to be specified via standard XML), but also streamlined the invocation of myriad Hibernate operations through the use of well-crafted abstractions founded on time-tested design patterns, such as HibernateTemplate and OpenSessionInView. We will discuss these core Spring-Hibernate integration details in the next few chapters. The important point here is that combining Spring and Hibernate affords developers an extremely powerful solution.

Not only does Spring simplify the integration of Hibernate, but it also reduces the coupling of Hibernate to an application. If the need arises to switch to a different ORM or persistence technology, this migration effort becomes much easier because there are few direct dependencies on Hibernate itself. For example, Spring provides a generic exception hierarchy for persistence-related errors. Although not required, it is considered good practice to convert Hibernate exceptions to Spring's generic exception hierarchy, which further decouples your application from Hibernate. Spring includes built-in mechanisms to simplify this conversion, to the point that it is fairly transparent. Additionally, Spring's integration code for other persistence technologies (such as JDBC, JPA, TopLink, etc.) will also handle the translation to Spring's generic exception hierarchy, further simplifying a migration from one persistence technology to another.

Establishing loosely coupled dependency relationships is one of Spring's core purposes. In fact, the framework itself limits direct coupling to itself as much as possible, meaning that your application will rarely be directly tied to Spring classes.

Best Practices for Architecting an Application

The more your code is abstracted away from interfacing directly with a database (and dealing with these lower-level concerns), the easier it is to switch to a different database or persistence technology. Similarly, Hibernate also offers an abstraction over your data model, allowing you to focus on your application's persistence details rather than on the particulars of your database. Through these decouplings, a persistence tier becomes far more portable across disparate databases.

Spring centralizes the wiring of dependencies within your application, making maintenance and configuration easier, and coercing developers to code to interfaces, which brings about cleaner and better code. It also allows you to focus more on your application's business logic, with less concern over how this information is physically stored and retrieved. This concept is often called layering. Each layer is focused specifically on accomplishing a particular task (with little knowledge or coupling to other layers within the application).

The Layers of a Persistence Tier

The application tier that deals with persistence is often called the persistence tier. Spring helps to enforce a modular architecture in which the persistence tier is divided into several core layers that contain the following:

  • The Domain Model

  • The Data Access Object (DAO) Layer

  • The Service Layer (or Service Façade)

Each of these layers is representative of proven design patterns that are key to building a solid, maintainable architecture. Outside the persistence tier, a typical Spring MVC application also has a controller layer, which handles user interaction, delegating to the service facade and shuttling necessary data back to the view. We will get into these implementation details over the next few chapters. Here, we'll take a brief look at the domain model, DAO, and service layers.

The Domain Model

The domain model represents the key entities within an application, defining the manner in which they relate to one another. Each entity defines a series of properties, which designates its characteristics, as well as its relationships to other entities. Each class within the domain model contains the various properties and associations that correlate to columns and relationships within the database. Typically, there is a domain entity for each table within the database, but this is not always the case.

For example, we might need to define a Person domain entity, designed to represent the concept of a person within the application and the database. The Person class could be represented as follows:

@Entity
public class Person implements Serializable {

    private Long id;
    private String firstName;
    private String lastName;
    private String username;
    private String password;

    private Integer roleLevel;

    private Integer version;


    public Person() {
}
    @Id
    public final Long getId() {
        return id;
    }
    @Version
    public Integer getVersion() {
        return version;
    }

     . . . Remaining Getters and Setters Omitted
}

Part of Hibernate's job is to convert between domain model instances and rows in the database. Developers provide hints to help Hibernate perform these conversions, by specifying mapping rules using XML or annotations. This metadata is used by Hibernate to define the characteristics of the domain model and how the object-oriented properties within a domain model class map to columns and relationships within the database.

Although XML was initially used to define mapping rules, we recommend using annotations as this approach is simpler and more concise. In fact, by applying the @Entity annotation to a class, it is assumed that a class property should be persisted to the database using the property name as the database column name and using the field type as a hint for the database column type. Of course, all these details can be explicitly configured or overridden, but thanks to sensible defaults, your mapping configuration should be relatively terse most of the time.

The Data Access Object (DAO) Layer

The DAO layer defines the means for saving and querying the domain model data. A DAO helps to abstract away those details specific to a particular database or persistence technology, providing an interface for persistence behavior from the perspective of the domain model, while encapsulating explicit features of the persistence technology. The goal of the DAO pattern is to completely abstract the underlying persistence technology and the manner in which it loads, saves, and manipulates the data represented by the domain model. The key benefit of the DAO pattern is separation of concerns—the lower-level details of the persistence technology and datasource are abstracted into a series of methods that provide querying and saving functionality. If the underlying persistence technology changes, most of the necessary changes would be limited to defining a new DAO implementation, following the same interface.

For example, we might create a PersonDAO class to define all the application's persistence needs related to the Person entity. In PersonDao, we would likely have a method like the following:

public Person getPersonById(Long id);

This method would be responsible for loading a Person entity from the database using its unique identifier.

The following might be another method for our application:

void savePerson(Person person);

This method would be designed to handle all updates to a given row in the Person table (that is, creation or modifications).

When defining a DAO, it is good practice to first write the interface, which delineates all the core persistence-related methods the application will need. We recommend creating separate DAOs for each persistent entity in your domain model, but there are no clear rules in this area. However, defining DAO methods in a separate interface is crucial, as it decouples the purpose and contract of the DAO from the actual implementation, and even allows you to write more than one implementation for a given DAO interface.

It's important to note that such an interface is agnostic to the persistence technology being used behind the scenes. In other words, the interface only depends on the relevant domain model classes, decoupling our application from the persistence details. Of course, the DAO implementation class will use Hibernate, JPA, or whatever persistence technology we have chosen to employ. However, the higher layers of our application are insulated from these details by the DAO interface, giving us portability, consistency, and a well-tiered architecture.

As we mentioned earlier, the Spring Framework also provides a generic data exception hierarchy, suitable for all types of persistence frameworks and usage. Within each persistence framework integration library, Spring does an excellent job of converting each framework-specific exception into an exception that is part of Spring's generic data-access exception hierarchy. All of the exceptions in Spring's generic exception hierarchy are unchecked, meaning your application code is not required to catch them. Spring helps to decouple your application from a particular persistence framework, allowing you to code to a generic and well-defined exception hierarchy that can be used with any persistence technology.

In Chapter 6, we will dive deeper into DAO implementation strategies, exploring the flexible querying and save/update capability afforded by Hibernate and JPA. Querying in particular can require quite a bit of complexity, and to this end, Hibernate and JPA provide two different approaches for searching and accessing your data. HQL and JPQL (Hibernate Query Language and Java Persistence Query Language, respectively) both offer an object-oriented syntax for expressing queries that is very similar to SQL. Although concise and intuitive, HQL and JPQL are interpreted at runtime, which means you cannot use the compiler to verify the correctness and integrity of a query.

To address this limitation, Hibernate also includes a Criteria API, which allows queries to be expressed programmatically. Until recently, JPA did not offer a Criteria API, which meant developers would have to go outside the JPA standard if they required this type of querying facility. However, with the introduction of JPA 2.0, a Criteria API is now available as part of the standard.

Whether to use HQL/JPQL or the Criteria API is sometimes a matter of style. However, there are some cases where the Criteria API is more effective and maintainable. For instance, if you are building a feature that requires dynamic filtering or ordering, being able to dynamically create a query programmatically, based on the user's runtime specifications, is much cleaner than attempting to dynamically generate a JPQL query string via concatenation. We will discuss these types of implementation decisions further in Chapter 6.

The Service Facade

The layer that handles the application business logic is (surprisingly enough) called the service layer. The service layer typically defines an application's public-facing API, combining one or more lower-level DAO operations into a single, cohesive transactional unit.

To help you understand how a service layer is built and used, let's take a look at a few examples:

Person loginUser(String username, String password);

The loginUser() method is intended to authenticate a user (that is, verify that the specified username and password match), and then load important user information into the session (grab user information, such as name, previous login date, role type, and so on). These tasks would likely not be handled by a single DAO method. Instead, we would probably build upon the functionality of two distinct DAO classes, a PersonDAO and a RoleDAO:

interface PersonDao {

    Person authenticatUser(String username, String password);

    . . .

    }



interface RoleDao {

    List<Role> getRolesForPerson(Person person);

    . . .

}

Together, these DAO methods accomplish a core business goal that is greater than the sum of its parts. In this example, we are using two read-only methods, but imagine a scenario in which we have a business method, such as the following:

boolean transferMoney(Long amount, Account fromAccount, Account destAccount)
  throws InvalidPermissionException, NotEnoughFundsException;

Now, assume that the preceding service layer method is composed of several DAO methods:

boolean validateSufficientFundsInAccount(Long accountId);

boolean removeFunds(Long accountId, Long amount);

boolean addFunds(Long accountId, Long amount);

It's easy to see what's going on here: we verify that enough cash exists in a particular account, and then pull the funds from one account and transfer them to another. The task is simple enough, but it doesn't take an overactive imagination to visualize the hysteria that might ensue should this business method fail halfway through the process—the funds might be withdrawn but never get deposited into the destination account. That might be good for the bank at first, but after a short while the entire economy collapses, and civilization is left with only a rudimentary barter system based on crazy straws and Star Wars action figures.

Leveraging Declarative Transactions

Service facade methods typically group together multiple DAO methods to accomplish business logic as a single unit of work. This is the concept of a transaction: the entire method (and all of its side effects) completes 100 percent successfully, or the application is rolled back to the state before the method was called. Before Spring persistence came on the scene, transactional requirements often prompted developers to look toward EJBs, which let them declaratively configure transactional semantics for each facade method. When they cannot specify transactional requirements declaratively, developers must instead use a programmatic approach. Not only does this add code complexity and obscure the intentions of the persistence logic, it further couples the persistence technology to the application. Transactional demarcation is often considered a cross-cutting concern, meaning it represents functionality that affects many parts of the codebase, but is orthogonal to their other features. Cross-cutting concerns add redundancy to code, since they need to be repetitively interweaved into the fabric of the business logic of an application, reducing code modularity. Aspect-Oriented Programming is aimed at solving this problem by allowing these concerns to be expressed once, and once only, as aspects, and then weaved into business logic as necessary.

In Spring, the service layer typically is intended to accomplish three primary tasks:

  • Serve as the core API through which other layers of your application will interface (this is the incarnation of the facade pattern)

  • Define the core business logic, usually calling on one or more DAO methods to achieve this goal

  • Define transactional details for each facade method

Understanding Aspect Oriented Programming (AOP)

The service layer is where Spring's AOP support is best utilized. Spring ships with transactional support that can be applied to application code through the use of interceptors that enhance your service layer code, by weaving in the transactional goodness. An interceptor is code that can be mixed into the execution flow of a method, usually delegating to the interceptor before and/or after a particular method is invoked. Simply speaking, an interceptor encapsulates the behavior of an aspect at a point in a method's execution.

It's not enough to specify that a method should be transactional. You shouldn't just force each method to occur within the confines of a transaction, rolling back if an error occurs and committing if all goes well. Perhaps certain methods don't attempt to modify any data, and therefore should execute within the context of a read-only transaction. Or more likely, perhaps some exceptions will trigger a rollback, while others will allow the transaction to carry on.

Pointcuts are another important component of Spring AOP. They help to define where a particular aspect (modularized functionality that can be weaved into application logic, such as transactional behavior) should be weaved. With Spring's transactional support, you have fine-grained control over which exceptions may trigger a commit or rollback, as well as the details over the transaction itself, such as determining the isolation level and whether a method should trigger a new transaction or a nested transaction, or execute within the existing transaction.

At a basic level, Spring accomplishes AOP through the use of the proxy design pattern. When you advise your classes by injecting cross-cutting behavior into various methods, you're not actually injecting code throughout your application (although in a way, that is the net effect of using AOP). Rather, you're requesting that Spring create a new proxy class, in which functionality is delegated to your existing class along with the transactional implementation (or whatever aspect you are trying to weave into your code). This explanation is an oversimplification of what actually happens under the hood, but the important thing to remember is that when you weave cross-cutting behavior into your classes via AOP, Spring is not directly inserting code; rather, it is replacing your classes with proxies that contain your existing code intertwined with the transactional code. Under the hood, this is implemented using JDK dynamic proxies or CGLIB bytecode enhancement.

Again, it's easy to see how this is a natural fit for a lightweight, IOC container like Spring. Since you're already entrusting Spring with handling your dependencies, it makes perfect sense to let Spring also take care of proxying these dependencies so you can layer on new cross-cutting behavior.

Although Spring AOP is amazingly powerful when you need to define and introduce new aspects to be weaved into your implementations, key transactional functionality is available out of the box and without the need to learn the details of AOP programming concepts. Still, understanding the basics of what Spring does under the hood is helpful. Keep in mind that AOP is useful for more than just applying transactional behavior—it is helpful for weaving any cross-cutting concern into your application, such as logging or security. We will discuss AOP in more detail later in this book.

Simplifying Transactions

Although applying transactions using Spring used to require a bit of AOP know-how, this process has been greatly simplified in recent versions of the framework. Now, applying transactional behavior to a service layer class is a matter of specifying the @Transactional annotation at either the class or method level. This annotation can be parameterized with attributes to customize its behavior, however the most significant detail is whether a transaction is read-only. Many developers don't recognize the importance of using transactions—even within a read-only context. Transactions can be useful for more than just ensuring atomicity. Transactions can also be used to specify a database isolation-level, and to delineate other contextual details that might be ambiguous outside a transactional scope. We strongly recommend that all database operations occur within the scope of some transaction—even if just to gain control over the contextual state of the database. We will discuss some of these details, such as understanding isolation levels and advanced transactional options, in Chapter 8.

The Benefit of Coding to Interfaces

We can rely on Spring to wire DAO dependencies into our service layer classes, ensuring that this integration happens in a consistent way and that the integration point between these two layers is through interfaces rather than specific implementation classes. As we mentioned earlier in this chapter, this is a fundamental concept for leveraging Spring's dependency injection: by coding to interfaces, we get more for our money. We can always rely on Spring to automatically inject required dependencies, but by using interfaces we gain the added benefit of being able to change which implementation should be injected at runtime. Without interfaces, there are no other options—we have hard-coded which dependencies must be injected into our components. Interfaces and Spring's dependency injection capability are a dynamic duo that offer significantly increased flexibility. For instance, without changing any code, you can choose to inject one set of dependencies for unit-testing and another in production deployment. Or you can choose which implementations to use for each environment. These are some of the benefits afforded by adherence to best practices and leveraging the Spring Framework.

Testing your Persistence Tier

As you'll see in later chapters, this separation of concerns helps keep your code clean and ensures that details from one layer don't interfere with the code from another layer. When it comes time for refactoring, this advantage can be significant. Perhaps even more important, these best practices are instrumental for ensuring an effective testing strategy. In Chapter 8, you will learn how Spring greatly simplifies the creation of unit and integration tests. When it comes to testing, it's rather intuitive to see how swapping implementations can really come in handy. Spring 3 includes a powerful TestContext framework that simplifies the creation and management of unit and integration tests—even abstracting away which test framework you happen to be using. Integration tests can often be a tricky matter, especially when you consider the details of instantiating all of a test's dependencies and components. For example, an integration test might require access to a database, as well as test data. Spring can bootstrap the ApplicationContext and then automatically inject any required dependencies. In the case of testing persistence-related code, you can choose to have your data occur within the scope of a transaction and then automatically rollback the transaction at the completion of the test to ensure that modifications made by the test are removed.

Advanced Features and Performance Tuning

This book will also cover some more advanced persistence concepts that are indispensable in most applications, such as optimization techniques for loading and managing complex relationships and collections within your domain model. We will discuss performance and optimization strategies, such as eager fetching and caching (at both the domain level and higher abstractions). As we mentioned earlier, Hibernate offers numerous features that can be leveraged to improve application performance. For instance, Hibernate and JPA offer a great deal of flexibility for tuning HQL/JPQL and Criteria API queries. These features enable developers to minimize round-trips to the database, allowing even large data sets to be accessed with minimal SQL queries. Hibernate also provides features such as lazy-loading and powerful caching mechanisms, which can be tuned to control the size and expiration time for cached entities. Understanding how these features work, as well as the myriad options available for controlling them, is critical for maximizing performance.

Caching is an often overlooked feature which can prevent an application from realizing its full potential. In the case of caching, it is either not fully utilized, or not enough time and attention are given to tuning and testing. However, if left untuned, caching can significantly degrade application performance. In Chapter 10, you will learn how Hibernate caching works, strategies for tuning and improving performance, and how to integrate a cache provider, such as ehcache. We will also explore several common pitfalls responsible for performance problems, such as the N+1 Selects issue, and how to go about identifying and resolving these issues.

Hibernate-Search

Sometimes, your application will require more than Hibernate or Spring have to offer. So we will discuss some important frameworks that extend Spring and Hibernate, such as Hibernate-Search. Hibernate-Search integrates the popular open source search framework, Lucene, into a Hibernate or JPA application. For features that require true search functionality, a relational database is not able to provide the capability that Lucene is able to offer. Hibernate-Search seamlessly integrates Lucene into your persistence tier, allowing you to execute Lucene queries within the scope of a Hibernate Session or a JPA Persistence Context. In Chapter 10, you will learn how this integration works, as well as the range of functionality afforded by Lucene and Hibernate-Search.

Building a REST Web Service

Since many applications use Spring and Hibernate as part of a web application, we will explore some of the potential issues and work-arounds related to building web applications. We will develop a REST-based web service, to explore some strategies for marshalling domain entities back and forth between Java and JSON or XML. We will examine frameworks, such as Dozer, which help to reduce some of the complexity related to serializing the object graph and dealing with potential LazyInitializationExceptions.

Other Persistence Design Patterns

Spring is based on time-tested design patterns, which go a long way toward simplifying code and reducing maintenance. While we're on the topic of some of the core building blocks of an application, let's look at a few of the more prevalent patterns used in much of the Spring architecture.

Note

You will see many of these patterns in action throughout this book, but it may be useful to take a look at the seminal work that popularized the use of patterns to solve recurring problems in object-oriented programming. This famous book is called Design Patterns: Elements of Reusable Object-Oriented Software, by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Addison-Wesley, 1994). The authors, and by association their patterns, are often jokingly referred to as "The Gang of Four".

The Template Pattern

The Template pattern is one of the most frequently used idioms within Spring's ORM framework integration packages. Spring provides templates for each of the most popular persistence frameworks, making it easy to port your code to a different persistence implementation. The Template Pattern is also used by the Spring framework to more effectively integrate JMS, define transactional behavior, and provide outbound email message capability, among other things.

The Template pattern allows a template to be defined in which a series of standard steps are followed, delegating to a subclass for those operations that are specific to the business logic. For example, when working with Hibernate, it is first necessary to create and initialize a new Hibernate session and optionally begin a transaction, before executing any Hibernate operations. When the operations are completed, it is necessary to close the session, and optionally commit or rollback the transaction. It would be rather redundant to repeat these same steps each time it was necessary to interface with Hibernate. Instead, we can leverage Spring's HibernateTemplate or JpaTemplate abstractions, which handle these steps for us. Although using these template support classes is an effective approach, we will explore alternative options later in this book.

Typically, a template is defined as an abstract class. To specify the operations to be wrapped within the templated workflow, we extend the template class, providing or extending the implementations for the abstract methods defined in the template parent class.

The Template pattern does exactly what its name implies: it extracts boilerplate and redundant tasks into a template, delegating to your specific implementation for functionality that can't be templated. In most cases, the code that cannot go in a template is your persistence logic itself. Using the Template pattern means you can focus on the database operations, without needing to worry about some of these mundane details:

  • Opening a database connection

  • Beginning a transaction

  • Wrapping your SQL operations in try-catch blocks (to handle unexpected exceptions)

  • Committing or rolling back a transaction

  • Closing the database connection (and handling any exceptions during this process)

  • Catching any exceptions that might occur in the transaction

Without using Spring, much of your code has little to do with your persistence logic, but is the same boilerplate code required by each and every operation.

Spring's HibernateTemplate and JpaTemplate offer a series of convenience methods to streamline much of the common persistence-related functionality. For example, the HibernateTemplate provides some useful methods such as:

  • saveOrUpdate(Object entity)

  • load(class entityClass, Serializable id)

  • find(String hqlQuery)

  • findByCriteria(DetachedCritieria criteria)

  • delete(Object entity)

HibernateTemplate offers quite a few more methods, as well as numerous permutations of some of the methods listed above. However, these convenience methods aren't direct examples of the template pattern. Rather, they are more like wrapper methods, which delegate to the core template method found in Spring's HibernateTemplate abstraction:

public Object execute(HibernateCallback action) throws DataAccessException {
           return doExecute(action, false, false);
         }

To execute a series of Hibernate operations, ensuring that they occur within the necessary templated steps (such as initializing and closing a Hibernate session), we need to create an anonymous implementation of the HibernateCallback interface, which is the single parameter to the preceding execute method. For example, to save an entity to the database, we could do the following:

public void customSavePerson(Person person) {
  getHibernateTemplate().execute(
    new HibernateCallback() {
      public Object doInHibernate(Session session) throws HibernateException {
        session.saveOrUpdate(person);
      }
    }
  );
}

Of course, it would be a lot simpler to just use HibernateTemplate's save(Object entity) method. Yet in this contrived example, we define an implementation of the HibernateCallback interface, which uses the passed-in Session to persist the Person entity to the database. Typically, this type of lower-level persistence functionality would be part of a DAO class, which helps to abstract the Hibernate-specific code from the rest of the application.

Although the HibernateTemplate and JpaTemplate provide an effective construct for streamlining persistence operations, they are no longer as necessary as they once were. Hibernate 3 shipped with a feature called Contextual Sessions, which provides greater flexibility around the scope of a Session. Part of what Spring's ORM support provides is the facilitation of a conversation surrounding persistence behavior, allowing Hibernate and JPA operations to be seamlessly integrated into Spring's transactional support. Spring's transactional features couldn't be properly utilized if every Hibernate operation created a new Session and a new database connection. To tie multiple lower-level persistence operations into a holistic "conversation," Spring uses the capabilities of ThreadLocal, allowing disparate operations to be scoped across a continuous thread. Recent versions of Hibernate provide a pluggable mechanism for defining how accessing the current Session should work. This new capability makes the HibernateTemplate and JpaTemplate a bit redundant in some circumstances. We will discuss the benefits and drawbacks of Spring's ORM templates in the next few chapters.

Note

Spring can be used for both JTA-managed transactions and local resource transactions. In a JTA environment, transactions are managed by the container, and offer additional behavior, such as distributed transactions. However, there is additional overhead for leveraging JTA transactions, and we recommend going with lighter-weight, local transactions if your application doesn't require the features provided by JTA. One of the advantages of using Spring is that switching between locally-managed transactions and JTA is just a matter of configuration. In the case of JTA, Spring will simply delegate to JTA, rather than manage the contextual state across an application thread.

The Active-Record Pattern

The DAO pattern isn't the only strategy for performing data operations. Another approach that has started to garner more attention recently is the Active-Record pattern. Active-Record is a design pattern popularized by frameworks such as Ruby on Rails and Grails, and takes a different approach than abstracting persistence functionality into a separate layer. Instead, Active-Record attempts to blend a domain object's behavior directly into the domain class itself.

Typically, an instance of a particular domain class represents a single row within the respective database table. To save changes to the instance (and thereby the appropriate row within the database), a save instance method is called directly on the instance. To delete an instance, we can simply invoke delete() on the instance that needs to be deleted. Query operations are usually invoked as static methods on the domain class itself. For example, in Grails, to find all Person entities with a lastName property of Fisher, we could call Person.findAllByLastName('Fisher').

The benefit of Active-Record is that it provides an intuitive and concise approach for performing persistence operations, and usually reduces code overhead significantly. Active-Record also attempts to combine behavior and properties into a domain object, providing a more object-oriented approach. You will learn more about the Active-Record pattern in Chapter 11, when we discuss Grails.

Summary

Throughout this book, we will demonstrate how Spring integrates with key persistence frameworks and strategies. Along the way, you will learn more about Spring's features and capabilities, and some of the key design patterns it uses to get the job done effectively.

Until several years ago, simple Java Database Connectivity (JDBC) was one of the most popular choices for implementing an application's persistence tier. However, EJB and open source ORM frameworks such as Hibernate have significantly changed the persistence landscape, by allowing developers to focus on a Java-based domain model, maintaining the object-oriented semantics of Java while still working with the relational concepts of a SQL database. ORM offers a level of abstraction that affords increased flexibility by decoupling application code from the lower-level details of a relational database.

However, things aren't always as easy as they seem. ORM is not without its drawbacks and consequences. First, as we mentioned earlier, there is the impedance mismatch between the object-oriented Java world and the relational SQL world. ORM frameworks, such as Hibernate, do their best to address this mismatch by offering extensive options for mapping between SQL and Java. Nevertheless, fundamental differences between these two spheres will always exist, and therefore can't be fully addressed.

Despite some of these limitations, ORM frameworks offer unparalleled benefits by streamlining the way in which developers work with a relational database. For instance, Hibernate introduces ancillary features, such as caching and lazy loading, which can improve the performance of an application dramatically with little or no additional coding effort. Hibernate and JPA also provide tools to seamlessly generate database schemas and even keep them in sync with the Java-based domain model. These features make the integration between application code and database even more seamless—to the point that it is often possible to forget that you are using a database altogether!

With an IoC container at its core, Spring helps to reduce application complexity, as well as coupling between classes, by handling the details necessary to integrate one dependency with another. Spring also provides transactional behavior, AOP capability, and infrastructural classes for numerous persistence frameworks, such as Hibernate and JPA.

Hibernate is an ORM framework intended to translate between relational databases and the realm of object-oriented development. Hibernate provides a querying interface, using Hibernate Query Language (HQL) or the Hibernate Criteria API. Together, Spring and Hibernate are a dynamic duo, capable of simplifying dependency collaboration, reducing coupling, and providing abstractions over persistence operations.

JPA is a Java standard for persistence, the design of which was significantly influenced by the Hibernate developers. Hibernate can be used as an implementation provider for JPA, allowing you to adhere to standards and gain framework portability, while still utilizing the excellent Hibernate implementation. However, there are some useful features that are not available in JPA, but are present only in the Hibernate implementation. With the release of JPA 2.0, many of the limitations of the JPA spec have been addressed, bringing more parity to Hibernate and JPA. For instance, JPA 2.0 now provides a Criteria API for querying in an object-oriented manner, and compile-time checking.

In this chapter, we outlined the foundational layers of a typical persistence tier, which is composed of the domain model, the DAO layer, and the service facade. We also discussed some integral design patterns leveraged by the Spring Framework, such as the Template design pattern. Although adhering to the typical foundational layers for your persistence tier is usually the best approach, some newer frameworks follow slightly different strategies, such as using the Active-Record pattern.

In the next chapter, we will build on the concepts and patterns introduced in this chapter as we incrementally build a Gallery application using Spring and Hibernate. Over the course of this book, it is our aim to illustrate time-tested and pragmatic best practices that we hope you will be able to use in your own applications as well.

Before we start coding, it's important to understand some of the core Spring and Hibernate concepts. So in the next chapter you will learn about Spring's architecture and capabilities, such as dependency injection, AOP, and persistence-related features.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.203.96