Backend Notes JUL 12, 2024 • 03 MIN READ

UNDERSTANDING THE REAL
DIFFERENCE BETWEEN
JPA AND HIBERNATE

Abstract technical background

When I first learned Spring Boot, I used Spring Data JPA everywhere. For a long time, I couldn’t see any difference between them. Honestly, I thought people were just overcomplicating it.

This article is what I wish someone had explained to me earlier.

Hibernate: The Engine That Actually Does the Work

Hibernate is a real ORM framework

It’s the thing that:

  • check_circle Converts Java objects into database rows
  • check_circle Tracks entity state (new, managed, detached)
  • check_circle Decides when to run SQL
  • check_circle Handles lazy loading, caching, dirty checking

If you use Hibernate directly, your code looks like this:

ConfigDB.java JAVA
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

User user = session.get(User.class, 1L);
user.setName("Joekawai");

tx.commit();
session.close();

You are very close to the database:

  • check_circle You manage sessions
  • check_circle You manage transactions
  • check_circle You control queries

Hibernate gives you power, but power comes with responsibility.

I’ll show it step by step so you see what Hibernate is really doing.

1. User Entity

User.java JAVA
@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String email;

    // getters & setters
}

Nothing special here — standard JPA annotations (Hibernate understands them).

2. Fetching List of Users with Hibernate (HQL)

This is the classic and most common way.

ConfigDB.java JAVA
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

List<User> users = session
    .createQuery("FROM User", User.class)
    .getResultList();

tx.commit();
session.close();

What’s happening behind the scenes:

  • check_circle Hibernate opens a Session
  • check_circle Converts FROM User → SQL (SELECT * FROM users)
  • check_circle Maps rows → User objects
  • check_circle Tracks them inside the persistence context

This is raw Hibernate power.

3. Fetching with Pagination

ConfigDB.java JAVA
Session session = sessionFactory.openSession();

List<User> users = session
    .createQuery("FROM User ORDER BY id", User.class)
    .setFirstResult(0)   // offset
    .setMaxResults(10)   // limit
    .getResultList();

session.close();

Hibernate translates this to:

User.sql JAVA
SELECT * FROM users
ORDER BY id
LIMIT 10 OFFSET 0;

You control performance explicitly.

4. Fetching with WHERE Condition

ConfigDB.java JAVA
Session session = sessionFactory.openSession();

List<User> users = session
    .createQuery(
        "FROM User u WHERE u.email LIKE :domain",
        User.class
    )
    .setParameter("domain", "%@gmail.com")
    .getResultList();

session.close();

This is HQL, not SQL

  • check_circle Uses entity name (User)
  • check_circle Uses field names (email)
  • check_circle Hibernate adapts it to your database dialect

5. Native SQL (When You Really Need Control)

Sometimes abstractions are not enough.

ConfigDB.java JAVA
Session session = sessionFactory.openSession();

List<User> users = session
    .createNativeQuery(
        "SELECT * FROM users WHERE email LIKE ?",
        User.class
    )
    .setParameter(1, "%@gmail.com")
    .getResultList();

session.close();

Hibernate still maps results → entities, but you own the SQL.

6. The Pain Point (Why Spring Data JPA Exists)

Notice what you had to manage:

  • check_circle Opening/closing sessions
  • check_circle Transactions
  • check_circle Query strings
  • check_circle Pagination logic

This is why Spring Data JPA lets you write:

UserRepository.java JAVA
List<User> findByEmailEndingWith(String domain);

Spring Data JPA: The Productivity Layer

Now here’s the important part many people misunderstand: Spring Data JPA is NOT an ORM.

It’s a Spring abstraction built on top of JPA, and JPA itself is only a specification. Hibernate is usually the implementation behind it.

With Spring Data JPA, you write this:

UserRepository.java JAVA
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
}

And then:

UserService.java JAVA
User user = userRepository.findById(1L).orElseThrow();
user.setName("Joekawai");
userRepository.save(user);

No session.
No transaction boilerplate.
No SQL (most of the time).

Spring handles all of that for you.

The Mental Model That Finally Made It Click

This is the simplest way to understand it:

  • check_circle Hibernate → How data is persisted
  • check_circle Spring Data JPA → How developers access data

In a typical Spring Boot application, the flow looks like this:

Sequence TXT
  Spring Data JPA
        ↓
      JPA
        ↓
   Hibernate
        ↓
    Database

When you call repository.save(entity) , Hibernate is the one generating SQL and talking to the database.

If Spring Data JPA Is So Easy, Why Learn Hibernate?

This is the trap many developers fall into — including me.

Spring Data JPA makes you productive fast. But Hibernate behavior still controls everything under the hood.
If you don’t understand Hibernate, you will eventually face:

  • check_circle N+1 query problems
  • check_circle Lazy loading errors
  • check_circle Mysterious performance degradation
  • check_circle “Why is this query running 20 times?”

Spring Data JPA hides complexity.
Hibernate explains it.

A junior developer uses Spring Data JPA.
A senior developer understands Hibernate while using Spring Data JPA.

When Should You Use Hibernate Directly?

Most of the time, you don’t need to.

Spring Data JPA is perfect for:

  • check_circle CRUD-heavy applications
  • check_circle Microservices
  • check_circle Most enterprise systems

You may go closer to Hibernate when:

  • check_circle You need very fine-grained performance tuning
  • check_circle You’re dealing with complex legacy schemas
  • check_circle You must control SQL generation precisely

Even then, Spring Data JPA usually stays — you just customize Hibernate behavior.

Final Takeaway

  • check_circle Hibernate is the engine that talks to the database
  • check_circle Spring Data JPA is the convenience layer that saves your time
  • check_circle They don’t compete — they cooperate
  • check_circle Mastery comes from knowing both, not choosing one

If this resonates with you, trust me — you’re already on the path from framework user to real backend engineer.

READ BEYOND THE VOID

Technical blueprint background
Backend Notes

ForkJoinPool vs ThreadPoolExecutor

What difference between ForkJoinPool and ThreadPoolExecutor...

Read Entry
Abstract digital network
Backend Notes

Difference List, ArrayList, HashMap, HashSet

What difference List, ArrayList, HashMap, HashSet...

Read Entry
Circuit board macro
Backend Notes

How ForkJoinPool Works Internally

How ForkJoinPool working...

Read Entry