Java EE Concepts: Interceptors, Hibernate, JPA, and Servlets

EJB Interceptors Explained

An interceptor is a class or method that intercepts EJB method calls or lifecycle events. It is used for common tasks like logging, security, transactions, and auditing. It helps avoid duplicate code and keeps business logic separate from system logic. Interceptors can be defined as a separate class or inside the bean. They use annotations to define behavior.

@AroundInvoke is used for method interception, while @PostConstruct and @PreDestroy are used for lifecycle interception. The @AroundInvoke method must return an Object and take an InvocationContext as a parameter. Inside it, pre-processing like logging can be done, then context.proceed() is called to execute the actual method, and finally, post-processing like auditing is performed.

Example flow: client calls bean → interceptor runs first (log start) → actual method executes → interceptor runs again (log end) → result returned to client.

Understanding Hibernate ORM

Hibernate is a Java ORM (Object Relational Mapping) framework that maps Java objects (entities) to database tables. It eliminates the need to write repetitive JDBC code for inserting, updating, or deleting data. Hibernate works with POJO classes and uses HQL (Hibernate Query Language) for database operations.

Hibernate Architecture

Hibernate Architecture consists of several components: Configuration, which reads hibernate.cfg.xml or hibernate.properties containing database connection details and mappings; SessionFactory, a heavy, thread-safe object created once per application to provide sessions; Session, a lightweight object representing a connection between the application and the database for performing CRUD operations; Transaction, which ensures data consistency using commit or rollback; Query Objects (HQL, SQL, or Criteria API) for executing queries; and Entities (POJOs) that map Java classes to database tables. The typical flow is: Application → Configuration → SessionFactory → Session → Transaction → Database.

The hibernate.cfg.xml File

The hibernate.cfg.xml file is the main configuration file. It includes database properties (driver, URL, username, password), the dialect (e.g., MySQLDialect, OracleDialect), mapping details (annotated classes or XML files), and other settings like hbm2ddl.auto for automatic table generation, show_sql to display executed SQL, and format_sql for readable output.

Key Features of Hibernate

  • Full ORM support
  • Database independence
  • Use of HQL for object-oriented queries
  • Automatic table creation
  • Caching for better performance
  • Lazy loading
  • Built-in transaction management
  • Support for both annotations and XML mappings

Core Components of Hibernate

The components of Hibernate are Configuration, SessionFactory, Session, Transaction, Query (HQL, SQL, Criteria), and Entities. These work together to simplify database operations.

Hibernate Configuration Components

These include database connection settings (driver, URL, credentials), SQL dialect, mapping resources, caching configurations (first-level and second-level), schema generation (hbm2ddl.auto), and transaction factory for JTA or JDBC management.

Advantages of Hibernate

Advantages include reduced JDBC boilerplate code, portability across databases, database-independent HQL queries, better performance with caching, automatic relationship handling, support for both XML and annotations, and easy integration with frameworks like Spring.

Disadvantages of Hibernate

However, disadvantages include a steep learning curve, slightly slower performance than raw JDBC in small apps, the need for configuration files, difficulty in debugging, and potential performance issues in large systems if not properly tuned.

Java Persistence Standards

In Java, persistence refers to the mechanism of storing and retrieving data from a database so that it can be used across different sessions or applications. Java provides several persistence standards to simplify database interaction and data management. The main and most widely used standard is the Java Persistence API (JPA), which is a specification that defines how Java objects (entities) are mapped to database tables using Object Relational Mapping (ORM). JPA does not provide an implementation itself but relies on providers such as Hibernate, EclipseLink, and OpenJPA to perform the actual database operations.

Before JPA, persistence in Java was mainly handled through JDBC (Java Database Connectivity) and Entity Beans in EJB (Enterprise JavaBeans). JDBC provides a low-level API for executing SQL queries directly, managing connections, and handling result sets, but it requires extensive boilerplate code and manual management of transactions. Entity Beans in EJB were used in older Java EE applications but were complex and difficult to maintain.

JPA improved upon these older approaches by providing a simplified and standardized ORM framework that allows developers to define entity classes using annotations like @Entity, @Id, and @Table. It introduced the EntityManager interface for managing entities and performing CRUD operations without writing complex SQL. JPA also supports JPQL (Java Persistence Query Language) and Criteria API for database-independent and dynamic queries.

Java Persistence API (JPA) Specification

Persistence means saving Java objects or data permanently into a storage system such as a database. In Java, this process is standardized and managed through the Java Persistence API (JPA), which provides a unified way to handle data persistence using Object Relational Mapping (ORM).

Java Persistence API (JPA) is a standard specification introduced in Java EE 5 that defines how Java objects are mapped to relational database tables. JPA itself is not a framework but an API (set of rules and interfaces) that different ORM providers implement. Popular implementations of JPA include Hibernate, EclipseLink, and OpenJPA.

The JPA Specification includes several core components and features. The Entity Class is a simple Java object (POJO) annotated with @Entity, representing a table in the database, where each field corresponds to a table column. The EntityManager is the main interface in JPA that manages the lifecycle of entities—performing operations like create, read, update, and delete—and is obtained from the EntityManagerFactory. The Persistence Unit, defined in the persistence.xml file, contains essential details such as the database driver, URL, username, password, and JPA provider. The Persistence Context acts as an environment or cache where entity objects are managed and synchronized with the database to maintain consistency.

JPA also provides JPQL (Java Persistence Query Language), an object-oriented query language that works with entities instead of database tables. For example: SELECT s FROM Student s WHERE s.age > 18. Annotations in JPA define the mapping between Java objects and database tables—@Entity declares a class as an entity, @Table maps it to a database table, @Id marks the primary key, and @GeneratedValue specifies automatic key generation. JPA also supports Transactions through the EntityTransaction interface, which ensures atomic operations using commit and rollback.

How JPA Works: Architecture and Flow

JPA (Java Persistence API) is a specification for Object Relational Mapping (ORM) in Java that defines how Java objects are stored, updated, and retrieved from a relational database. It does not provide implementation itself but uses providers like Hibernate, EclipseLink, or OpenJPA. JPA works by mapping Java entity classes to database tables. First, developers define an Entity Class by annotating a POJO with @Entity, @Id, and @Column. Then, configuration details such as the persistence unit, database connection, and provider are defined in the persistence.xml file. The EntityManagerFactory is created once per application and is responsible for generating EntityManager instances. The EntityManager manages entity objects and performs CRUD (Create, Read, Update, Delete) operations, while the EntityTransaction handles commit and rollback operations to maintain data consistency. The actual data is stored in the Database.

The JPA Architecture consists of several components: the Entity, which represents a table in the database; the EntityManager, which manages entity lifecycle operations like persist, remove, find, and merge; the EntityManagerFactory, which produces EntityManager objects; and the Persistence Unit, defined in persistence.xml, which contains database connection information, JPA provider details, and mapped entity classes. The Persistence Context acts as a cache for entity objects managed by the EntityManager and keeps them synchronized with the database. Queries can be executed using JPQL (Java Persistence Query Language) or the Criteria API for dynamic queries. The Transaction component ensures that operations follow commit and rollback rules, maintaining data integrity, while the Database serves as the final data storage system.

In a simple flow, the process is: Application → EntityManagerFactory → EntityManager → Persistence Context → Entities ↔ Database, where JPA acts as a bridge between Java objects and the relational database, simplifying and standardizing database operations in Java applications.

Solving Impedance Mismatch with ORM

Impedance Mismatch occurs when object-oriented applications (like Java programs using classes, objects, and inheritance) interact with relational databases (which store data in tables, rows, and columns). Since both use different data models and principles, inconsistencies arise in how data is stored and retrieved. This difference between the object model and the relational model leads to various challenges in data mapping and management.

Common problems include the fact that objects use references, while databases use foreign keys to represent relationships. Classes support inheritance, but relational databases have no direct concept of inheritance. Additionally, data types in Java (like boolean, Date, or enums) may not directly match the data types used in SQL databases, requiring conversion during storage and retrieval.

The solution to impedance mismatch is the use of Object Relational Mapping (ORM) frameworks such as Hibernate or JPA. These frameworks automatically handle the mapping between Java classes and database tables, object properties and table columns, and object relationships.

JSTL: JavaServer Pages Standard Tag Library

JSTL (JavaServer Pages Standard Tag Library) is a collection of standard JSP tags that provides common functionalities like iteration, conditionals, formatting, XML handling, and database access without using Java scriptlets. It simplifies JSP development by making pages cleaner, readable, and easier to maintain, while promoting a standard approach across all Java web applications.

JSTL helps developers perform tasks such as looping, condition checking, internationalization, and database operations directly in JSP pages through predefined tags, improving productivity and reducing code complexity.

Advantages of JSTL

  • Eliminates Java code (scriptlets) from JSP pages.
  • Enhances readability and maintainability of code.
  • Promotes reusability of tag logic across multiple pages.
  • Follows standard specifications supported by all JSP containers.
  • Fully integrates with Expression Language (EL) for easy variable access from scopes like page, request, session, and application.

JSTL Tag Libraries and Their Paths

  1. Core Tags (c)
    Used for general operations like iteration, conditionals, setting variables, and URL handling.
    Example Tags: <c:out>, <c:set>, <c:if>, <c:forEach>
    URI Path: http://java.sun.com/jsp/jstl/core
    Taglib Directive: <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

  2. Formatting & Internationalization Tags (fmt)
    Used for formatting numbers, dates, and messages according to locale.
    Example Tags: <fmt:formatNumber>, <fmt:message>
    URI Path: http://java.sun.com/jsp/jstl/fmt
    Taglib Directive: <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

  3. SQL Tags (sql)
    Used for database operations like queries and updates (mainly for learning or demos).
    Example Tags: <sql:query>, <sql:update>
    URI Path: http://java.sun.com/jsp/jstl/sql
    Taglib Directive: <%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>

  4. XML Tags (x)
    Used for parsing and processing XML data.
    Example Tags: <x:parse>, <x:out>, <x:forEach>
    URI Path: http://java.sun.com/jsp/jstl/xml
    Taglib Directive: <%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>

The RequestDispatcher Interface in Servlets

The RequestDispatcher interface is part of the javax.servlet package. It is used to forward a request from one servlet or JSP to another server-side resource, such as another servlet, JSP, or HTML file. It can also be used to include the content of another resource within the current response. Importantly, the client remains unaware of this internal forwarding or inclusion process—it all happens on the server side.

To obtain a RequestDispatcher object, there are two common ways:

  • Using the request object:
    RequestDispatcher rd = request.getRequestDispatcher("target.jsp");
  • Using the ServletContext:
    RequestDispatcher rd = getServletContext().getRequestDispatcher("/target.jsp");

The RequestDispatcher interface provides two main methods:

  1. void forward(ServletRequest request, ServletResponse response)
    This method forwards the current request and response to another resource on the server. After forwarding, the original servlet stops processing further output. The client’s browser is not redirected, so the URL remains unchanged. It is commonly used for server-side redirection.

  2. void include(ServletRequest request, ServletResponse response)
    This method includes the output of another resource (such as a JSP or servlet) into the current response. The original resource continues processing after the inclusion. It is useful for including common page elements like headers, footers, or navigation menus.

EJB Remote vs. Local Interfaces

In EJB, interfaces define how clients interact with beans. A Remote Interface (marked with @Remote) is used when a bean is accessed from a different JVM or server, allowing distributed communication between applications over a network. It provides flexibility to share business logic across servers but adds overhead due to network calls and object serialization. A Local Interface (marked with @Local) is used when the bean and client run within the same JVM. It enables faster, direct method calls without network or serialization overhead, making it ideal for modules within the same application (like a servlet accessing an EJB). However, local interfaces cannot be accessed by remote clients.

The javax.servlet Package

The javax.servlet package is a core part of Java EE (Jakarta EE) that provides classes and interfaces for developing Servlets. A Servlet is a Java program that runs on a web server and is used to handle client requests and generate dynamic web content such as HTML, JSON, or XML. It forms the foundation of Java web application development.

The javax.servlet package contains several important interfaces that define the behavior and lifecycle of a servlet. The main one is the Servlet Interface, which every servlet must implement either directly or by extending GenericServlet or HttpServlet. It defines methods like init(ServletConfig config) for initialization, service(ServletRequest req, ServletResponse res) for handling requests, and destroy() for cleanup when the servlet is taken out of service. Additional methods include getServletConfig() for configuration details and getServletInfo() for metadata about the servlet.

The ServletRequest Interface provides methods to access client data such as form inputs, query parameters, and HTTP headers. The ServletResponse Interface allows sending responses back to the client, including HTML pages or other content formats. The ServletConfig Interface supplies initialization parameters defined in the deployment descriptor (web.xml), while the ServletContext Interface represents the web application’s environment and allows servlets to share information globally within the same application.