Relationship between Spring Data JPA, JPA, Hibernate

Udaykishore Resu
5 min readOct 25, 2024

--

Relationship between Spring Data JPA, JPA, Hibernate

Hibernate is an implementation of the JPA specification for object-relational mapping, while Spring Data JPA is an abstraction layer that reduces boilerplate code for data access. Hibernate generates SQL statements behind the scenes, whereas Spring Data JPA simplifies CRUD operations using repository interfaces, relying on JPA providers like Hibernate for functionality.

Let’s break down each layer and explore their dependencies and potential for independent use.

Layer Breakdown

Spring Data JPA

Spring Data JPA is the highest-level abstraction in this stack. It provides a simplified interface for working with databases, reducing boilerplate code significantly.

JPA (Java Persistence API)

JPA is a specification that defines a standard interface for object-relational mapping in Java applications.

Hibernate

Hibernate is an implementation of the JPA specification. It’s an ORM (Object-Relational Mapping) tool that handles the low-level details of persisting Java objects to a relational database.

Database

This is the actual relational database where data is stored and retrieved from.

Dependencies and Independent Use

While these layers are often used together, they can be used independently to some extent. Here’s a breakdown of their dependencies:

1. Spring Data JPA: Depends on JPA, but not necessarily on Hibernate.

2. JPA: Can be used without Spring Data JPA or Hibernate.

3. Hibernate: Can be used without Spring Data JPA but implements JPA.

4. Database: Can be accessed directly without any of the above layers.

Examples of Independent Use

Using JPA without Spring Data JPA

You can use JPA directly without Spring Data JPA. This involves more boilerplate code but gives you finer control:

EntityManager em = entityManagerFactory.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
User user = new User("John Doe");
em.persist(user);
tx.commit();
em.close();

Using Hibernate without Spring Data JPA

Hibernate can be used directly without Spring Data JPA:

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
User user = new User("Jane Doe");
session.save(user);
tx.commit();
session.close();

Using Spring Data JPA

Spring Data JPA simplifies data access significantly:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
// In a service class
@Autowired
private UserRepository userRepository;
public void saveUser() {
User user = new User("Alice");
userRepository.save(user);
}

Internal components of Hibernate, Spring Data JPA, and JPA:

Hibernate:

Hibernate is an Object-Relational Mapping (ORM) framework that implements the JPA specification. Its key internal components include:

Configuration

The Configuration object is the first Hibernate object created in any Hibernate application. It’s responsible for:

  • Reading and parsing configuration files
  • Creating a SessionFactory

SessionFactory

SessionFactory is a thread-safe, immutable object that:

  • Acts as a factory for Session objects
  • Holds second-level cache of data
  • Typically created once during application initialization

Session

A Session represents a single-threaded unit of work. It:

  • Provides an interface between the application and Hibernate
  • Wraps a JDBC connection
  • Is the primary interface for persisting and retrieving objects

Transaction

Represents a unit of work. It’s optional but recommended for maintaining data integrity.

Query and Criteria

These are used to retrieve data from the database using HQL (Hibernate Query Language) or Criteria API.

JPA (Java Persistence API)

JPA is a specification for ORM in Java applications. Its key components include:

EntityManagerFactory

Like Hibernate’s SessionFactory, it’s a thread-safe object that creates EntityManager instances.

EntityManager

Analogous to Hibernate’s Session, it manages a set of entities and is the primary interface for persisting objects.

Entity

A lightweight persistent domain object representing a table in the database.

EntityTransaction

Allows operations to be grouped into units of work that can either succeed or fail as a unit.

Query

Represents database queries written in JPQL (Java Persistence Query Language) or native SQL.

Spring Data JPA

Spring Data JPA is an abstraction layer on top of JPA that simplifies data access. Its key components include:

Repository

An interface that provides CRUD operations for a specific entity. Spring Data JPA generates implementations automatically.

JpaRepository

An extension of Repository that provides additional methods like pagination and sorting.

@Query Annotation

Allows defining custom queries using JPQL or native SQL.

Specifications

A way to create dynamic queries programmatically.

Auditing

Provides support for tracking who created or modified entities and when.

In practice, these components work together as follows:

  1. Spring Data JPA provides high-level abstractions (like Repository interfaces) that developers interact with directly.
  2. These abstractions use JPA’s EntityManager under the hood to perform database operations.
  3. JPA, in turn, typically uses Hibernate as its implementation.
  4. Hibernate then generates and executes the actual SQL statements.

This layered architecture allows developers to work at a high level of abstraction while still having the flexibility to drop down to lower levels when needed for more complex operations.

Let’s walk through a practical example that demonstrates how these components work together in a typical Spring Data JPA application. We’ll create a simple application to manage books in a library.

Entity

First, we define our entity:

@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String author;
// Constructors, getters, and setters omitted for brevity
}

Repository

Next, we create a repository interface:

public interface BookRepository extends JpaRepository<Book, Long> {
List<Book> findByAuthor(String author);
}

Service

Then, we create a service class to handle business logic:

@Service
public class BookService {
@Autowired
private BookRepository bookRepository;
public Book addBook(Book book) {
return bookRepository.save(book);
}
public List<Book> findBooksByAuthor(String author) {
return bookRepository.findByAuthor(author);
}
public void deleteBook(Long id) {
bookRepository.deleteById(id);
}
}

Controller

Finally, we create a controller to handle HTTP requests:

@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@PostMapping
public ResponseEntity<Book> addBook(@RequestBody Book book) {
Book savedBook = bookService.addBook(book);
return ResponseEntity.ok(savedBook);
}
@GetMapping("/author/{author}")
public ResponseEntity<List<Book>> getBooksByAuthor(@PathVariable String author) {
List<Book> books = bookService.findBooksByAuthor(author);
return ResponseEntity.ok(books);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteBook(@PathVariable Long id) {
bookService.deleteBook(id);
return ResponseEntity.noContent().build();
}
}

Now, let’s break down how these components work together:

1. When a request comes in to add a book, the BookController calls bookService.addBook(book).

2. The BookService then calls bookRepository.save(book).

3. Spring Data JPA’s JpaRepository implementation handles this call, using JPA’s EntityManager under the hood.

4. The EntityManager uses Hibernate (the JPA implementation) to generate and execute the appropriate SQL INSERT statement.

5. For the findByAuthor method, Spring Data JPA automatically generates the implementation based on the method name, which Hibernate then translates into a SQL SELECT statement.

6. For the deleteBook method, Spring Data JPA’s deleteById method is used, which Hibernate translates into a SQL DELETE statement.

This layered approach allows developers to work primarily with high-level abstractions (Repository interfaces and Service classes) while the underlying JPA and Hibernate layers handle the complexities of database interactions.

EntityManager

For more complex operations, you can always inject the EntityManager directly into your service or repository:

@Repository
public class CustomBookRepository {
@PersistenceContext
private EntityManager entityManager;
public List<Book> findExpensiveBooksByAuthor(String author, BigDecimal price) {
String jpql = "SELECT b FROM Book b WHERE b.author = :author AND b.price > :price";
return entityManager.createQuery(jpql, Book.class)
.setParameter("author", author)
.setParameter("price", price)
.getResultList();
}
}

--

--

Udaykishore Resu
Udaykishore Resu

Written by Udaykishore Resu

Senior Software Engineer with 11+ years in cloud tech, API design, and microservices. Expertise in Golang, Java, Scala. AWS certified. Based in Atlanta, USA.

No responses yet