2. Layered Architecture of a Java Application
A Java application is often divided into layers, each with a well-defined role. Let’s consider a common architecture, the three-tier architecture:
![]() |
- Layer [1], referred to here as [ui] (User Interface), is the layer that interacts with the user via a Swing GUI, a console interface, or a web interface. Its role is to provide data from the user to layer [2] or to present data provided by layer [2] to the user.
- Layer [2], referred to here as [business], is the layer that applies the so-called business rules—i.e., the application’s specific logic—without concerning itself with where the data it receives comes from or where the results it produces go.
- Layer [3], referred to here as [DAO] (Data Access Object), is the layer that provides layer [2] with pre-stored data (files, databases, etc.) and stores some of the results provided by layer [2].
There are various ways to implement the [DAO] layer. Let’s examine a few of them:
![]() |
The [JDBC] layer above is the standard layer used in Java to access databases. It isolates the [DAO] layer from the DBMS that manages the database. Theoretically, one can switch DBMSes without changing the [DAO] layer’s code. Despite this advantage, the JDBC API has certain drawbacks:
- All operations on the DBMS are likely to throw the checked SQLException. This forces the calling code (the [DAO] layer in this case) to wrap them in try/catch blocks, thereby making the code quite verbose.
- the [DAO] layer is not completely independent of the DBMS. For example, DBMSs have proprietary methods for automatically generating primary key values that the [DAO] layer cannot ignore. Thus, when inserting a record:
- With Oracle, the [DAO] layer must first obtain a value for the record’s primary key and then insert the record.
- With SQL Server, the [DAO] layer inserts the record, which is automatically assigned a primary key value by the DBMS, a value returned to the [DAO] layer.
These differences can be eliminated by using stored procedures. In the previous example, the [DAO] layer will call a stored procedure in Oracle or SQL Server that accounts for the DBMS’s specific features. These will be hidden from the [DAO] layer. Nevertheless, while changing the DBMS will not require rewriting the [DAO] layer, it will still require rewriting the stored procedures. This may not be considered a deal-breaker.
Numerous efforts have been made to isolate the [DAO] layer from the proprietary aspects of DBMSs. One solution that has been highly successful in this area in recent years is Hibernate:
![]() |
The [Hibernate] layer sits between the [DAO] layer written by the developer and the [JDBC] layer. Hibernate is an ORM (Object-Relational Mapper), a tool that bridges the relational world of databases with that of objects manipulated by Java. The developer of the [DAO] layer no longer sees the [JDBC] layer or the database tables whose content they wish to utilize. They see only the object representation of the database, provided by the [Hibernate] layer. The bridge between the database tables and the objects manipulated by the [DAO] layer is primarily established in two ways:
- via XML-style configuration files
- through Java annotations in the code, a technique available only since JDK 1.5
The [Hibernate] layer is an abstraction layer designed to be as transparent as possible. The ideal scenario is for the [DAO] layer developer to be completely unaware that they are working with a database. This is feasible if they are not the one writing the configuration that bridges the relational and object-oriented worlds. Configuring this bridge is quite delicate and requires some experience.
The [4] object layer, which mirrors the database, is called the "persistence context." A [DAO] layer based on Hibernate performs persistence operations (CRUD: create, read, update, delete) on the objects in the persistence context; these operations are translated by Hibernate into SQL statements executed by the JDBC layer. For database query operations (SQL SELECT), Hibernate provides developers with an HQL (Hibernate Query Language) to query the persistence context [4] rather than the database itself.
Hibernate is popular but complex to master. The learning curve, often presented as easy, is in fact quite steep. As soon as you have a database with tables featuring one-to-many or many-to-many relationships, configuring the relational/object bridge is beyond the reach of the average beginner. Configuration errors can lead to poor-performing applications.
In light of the success of ORM products, Sun, the creator of Java, decided to standardize an ORM layer via a specification called JPA (Java Persistence API), which was released alongside Java 5. The JPA specification has been implemented by various products: Hibernate, TopLink, EclipseLink, OpenJPA, etc. With JPA, the previous architecture becomes the following:
![]() |
The [DAO] layer now interacts with the JPA specification, a set of interfaces. Developers have gained in terms of standardization. Previously, if a developer changed their ORM layer, they also had to change their [DAO] layer, which had been written to interact with a specific ORM. Now, they will write a [DAO] layer that interacts with a JPA layer. Regardless of which product implements the JPA layer, the interface presented to the [DAO] layer remains the same.
In this document, we will use a [DAO] layer built on top of a JPA/Hibernate or JPA/EclipseLink layer. We will also use the Spring 2.8 framework to link these layers together.
![]() |
The main advantage of Spring is that it allows you to link layers through configuration rather than in the code. Thus, if the JPA/Hibernate implementation needs to be replaced with a Hibernate implementation without JPA—for example, because the application is running in a JDK 1.4 environment that does not support JPA—this change in the [DAO] layer’s implementation has no impact on the [business] layer’s code. Only the Spring configuration file that links the layers together needs to be modified.
With Java EE 5, another solution exists: implementing the [business] and [DAO] layers using EJB3 (Enterprise JavaBeans version 3):
![]() |
We will see that this solution is not very different from the one using Spring. The Java EE 5 environment is available within so-called application servers such as Sun Application Server 9.x (Glassfish), JBoss Application Server, Oracle Container for Java (OC4J), ... An application server is essentially a web application server. There are also so-called "stand-alone" EE 5 environments, i.e., those that can be used outside of an application server. This is the case with JBoss EJB3 or OpenEJB.
In an EE5 environment, the layers are implemented by objects called EJBs (Enterprise Java Beans). In previous versions of EE, EJBs (EJB 2.x) were known to be difficult to implement and test, and sometimes underperformed. A distinction is made between EJB 2.x "entity" beans and EJB 2.x "session" beans. In short, an EJB 2.x "entity" corresponds to a database table row, and an EJB 2.x "session" is an object used to implement the [business logic] and [DAO] layers of a multi-layer architecture. One of the main criticisms of layers implemented with EJBs is that they can only be used within EJB containers, a service provided by the EE (Enterprise Edition) environment. This environment, which is more complex to set up than an SE (Standard Edition) environment, can discourage developers from testing frequently. Nevertheless, there are Java development environments that facilitate the use of an application server by automating the deployment of EJBs to the server: Eclipse, NetBeans, JDeveloper, IntelliJ IDEA. Here, we will use NetBeans 6.8 and the GlassFish v3 application server.
The Spring framework was created in response to the complexity of EJB2. In an SE environment, Spring provides a significant number of the services typically offered by EE environments. For example, in the "Data Persistence" section, Spring provides the connection pools and transaction managers that applications require. The emergence of Spring has fostered a culture of unit testing, which has become easier to implement in the SE context than in the EE context. Spring allows the implementation of application layers using standard Java objects (POJO, Plain Old/Ordinary Java Object), enabling their reuse in other contexts. Finally, it integrates numerous third-party tools fairly transparently, notably persistence tools such as Hibernate, EclipseLink, iBatis, ...
Java EE 5 was designed to address the shortcomings of the EJB 2 specification. EJB 2.x has evolved into EJB 3. These are POJOs annotated with tags that make them special objects when they are within an EJB 3 container. Within the container, the EJB3 can leverage the container’s services (connection pool, transaction manager, etc.). Outside the EJB3 container, the EJB3 becomes a standard Java object. Its EJB annotations are ignored.
Above, we have depicted Spring and an EJB3 container as a possible infrastructure (framework) for our multi-layer architecture. It is this infrastructure that will provide the services we need: a connection pool and a transaction manager.
- With Spring, the layers will be implemented using POJOs. These will have access to Spring’s services (connection pool, transaction manager) through dependency injection into these POJOs: when constructing them, Spring injects references to the services they will need.
- With the EJB3 container, the layers will be implemented using EJBs. A layered architecture implemented with EJB3s is not very different from one implemented with POJOs instantiated by Spring. We will find many similarities.
- Finally, we will present an example of a multi-layered web application:
![]() |






