Java Persistence Frameworks
Almost every Java application needs to save data to a database and read it back. Java persistence frameworks do the heavy lifting, so you aren't writing hundreds of lines of raw JDBC code. This guide explains the main options (Hibernate, JPA, Spring Data JPA, MyBatis, jOOQ, and more), shows the same query in each, and helps you pick the right one.
What is a Java persistence framework?
"Persistence" means data that outlives your program: it's still there after a restart because it's stored in a
database. A persistence framework connects your Java objects to database
tables. It's like a translator between two languages: your code speaks in objects (book.getTitle()), the
database speaks in rows and SQL, and the framework converts between them.
Why not just use JDBC directly?
You can. JDBC is Java's built-in database API, and every framework below uses it underneath. But raw JDBC means opening connections, writing SQL strings, copying each column into a field, and closing everything carefully. Frameworks remove that repetition and many of the bugs that come with it.
ORM vs SQL-first: the two styles
| ORM (object-relational mapping) | SQL-first | |
|---|---|---|
| Idea | Map classes to tables; the framework writes most SQL for you | You write SQL; the framework runs it and maps results |
| Examples | Hibernate, EclipseLink, Spring Data JPA | MyBatis, jOOQ, Spring JdbcClient |
| Strengths | Less code for everyday work; database-independent | Full control; predictable performance; any SQL feature |
| Watch out for | Hidden queries (the "N+1 problem"), a learning curve | More SQL to write and maintain |
JPA vs Hibernate vs Spring Data JPA: what's the difference?
These three names confuse almost every beginner, because they're layers that work together:
The persistence stack in a typical Spring Boot app
- Your code bookRepository.findAll()
- Spring Data JPA writes repository code for you (optional layer)
- JPA / Jakarta Persistence the standard: @Entity, EntityManager (an interface)
- Hibernate the implementation that does the real work
- JDBC → your database MySQL, PostgreSQL, …
- Hibernate the implementation that does the real work
- JPA / Jakarta Persistence the standard: @Entity, EntityManager (an interface)
- Spring Data JPA writes repository code for you (optional layer)
JPA is like a power-socket standard; Hibernate is a plug that fits it (EclipseLink is another). Spring Data JPA is an adapter on top that saves you from writing the same repository code over and over.
A JPA entity is just a class with annotations telling the framework how it maps to a table:
import jakarta.persistence.*;
@Entity // this class maps to a table (named "book" by default)
public class Book {
@Id
@GeneratedValue // let the database generate the id
private Long id;
private String title;
@Column(name = "published_year")
private int publishedYear;
protected Book() { } // JPA needs a no-argument constructor
public Book(String title, int publishedYear) {
this.title = title;
this.publishedYear = publishedYear;
}
public String getTitle() { return title; }
public int getPublishedYear() { return publishedYear; }
} @Entity— marks the class as persistent. Each object becomes one row.@Idand@GeneratedValue— the primary key, generated automatically.@Column(name = "published_year")— maps a field to a differently named column.jakarta.persistence— modern package name. Older code usesjavax.persistence; the ideas are identical.
The main Java persistence frameworks
1. Hibernate ORM
The most widely used Java ORM and the most popular JPA implementation. It's the default persistence provider in both Spring Boot and Quarkus. Beyond standard JPA, it adds caching, batch processing, and many tuning options. hibernate.org
2. Spring Data JPA
Not an ORM itself, but the most common way Spring developers use one. Declare an interface such as
BookRepository, and Spring generates methods like save, findById, and
queries derived from method names.
spring.io
3. MyBatis
A SQL mapper: you write the SQL (in annotations or XML files) and MyBatis maps the results to objects. Popular for complex queries, stored procedures, and databases designed long before the Java code. mybatis.org
4. jOOQ
Generates Java classes from your database schema so you can write SQL as type-safe Java code. If a column is renamed, your code stops compiling instead of failing in production. Free for open-source databases; commercial databases need a paid licence. jooq.org
5. EclipseLink
Another full JPA implementation, originally the official reference implementation of JPA. It's the default in
GlassFish and Payara application servers. Because both follow the JPA standard, entity code like the
Book class above works with either.
eclipse.dev/eclipselink
6. Spring JDBC (JdbcClient / JdbcTemplate)
A thin, friendly layer over JDBC with no ORM. You write SQL, and Spring handles connections, parameters, and errors.
JdbcClient (Spring Framework 6.1+) is the modern fluent API; JdbcTemplate is the long-standing original.
Older options you may meet in existing code
- Apache OpenJPA — another JPA implementation, historically used by Apache TomEE. Rarely chosen for new projects.
- Apache Torque — an early ORM that generates classes from an XML schema. Mostly found in legacy applications.
The same query in 5 frameworks
Here's one task, "find all books published after 2000", written in each style. Switch between them:
Books published after 2000
JPA / Hibernate
Query objects with JPQL, which looks like SQL but uses class and field names. Hibernate translates it into SQL for your database.
List<Book> books = entityManager
.createQuery("SELECT b FROM Book b WHERE b.publishedYear > :year", Book.class)
.setParameter("year", 2000)
.getResultList(); Spring Data JPA
Just declare a method. Spring reads its name and generates the query for you.
public interface BookRepository extends JpaRepository<Book, Long> {
List<Book> findByPublishedYearGreaterThan(int year);
}
// usage
List<Book> books = bookRepository.findByPublishedYearGreaterThan(2000); MyBatis
You write the SQL; MyBatis runs it and maps each row to a Book.
@Mapper
public interface BookMapper {
@Select("""
SELECT id, title, published_year AS publishedYear
FROM book WHERE published_year > #{year}
""")
List<Book> findNewerThan(int year);
} jOOQ
SQL written as Java. BOOK is generated from your database schema, so a typo in a column name is a compile error.
List<BookRecord> books = dsl.selectFrom(BOOK)
.where(BOOK.PUBLISHED_YEAR.gt(2000))
.fetch(); Spring JdbcClient
Plain SQL with a friendly API (Spring Framework 6.1+). No ORM, no surprises.
List<Book> books = jdbcClient
.sql("SELECT * FROM book WHERE published_year > :year")
.param("year", 2000)
.query(Book.class)
.list(); Which Java persistence framework should you choose?
What fits your project?
Standard Spring Boot app
Use Spring Data JPA (with Hibernate underneath).
- Repository interfaces with almost no code
- The default, best-documented path in Spring Boot
- Drop down to native SQL for the tricky queries
I want full control of SQL
Use MyBatis or jOOQ.
- MyBatis: write SQL in annotations or XML, and it maps rows to objects
- jOOQ: write SQL as type-safe Java code checked by the compiler
- Great for complex reporting queries and legacy schemas
Small app, few tables
Use Spring JdbcClient (or plain JDBC).
- No ORM concepts to learn: just SQL in, objects out
- Very little hidden behaviour
- Easy to move to JPA later if the app grows
Jakarta EE / Quarkus
Use Jakarta Persistence (JPA) via Hibernate, or Hibernate with Panache in Quarkus.
- Standard annotations that work across servers
- GlassFish and Payara use EclipseLink instead; your JPA code stays the same
Comparison table
| Framework | Style | You write SQL? | Learning curve | Best for |
|---|---|---|---|---|
| Hibernate | ORM (JPA) | Rarely | Medium–High | Rich domain models, most business apps |
| Spring Data JPA | ORM layer | Rarely | Low to start | Spring Boot CRUD and typical apps |
| MyBatis | SQL mapper | Yes | Low | Complex SQL, legacy schemas |
| jOOQ | Type-safe SQL | Yes (as Java) | Medium | SQL-heavy apps wanting compile-time safety |
| EclipseLink | ORM (JPA) | Rarely | Medium–High | Jakarta EE servers (GlassFish, Payara) |
| Spring JdbcClient | Thin JDBC | Yes | Very low | Small apps, simple queries |
The N+1 problem: with an ORM, looping over 100 orders and touching each order's customer can quietly run 101 queries. Learn to spot it in your SQL logs and fix it with a "fetch join" or an entity graph. It's the most common ORM performance issue.
Quick recap
- Persistence frameworks save Java objects to databases without raw JDBC boilerplate.
- JPA is the standard, Hibernate implements it, and Spring Data JPA adds ready-made repositories on top.
- ORMs (Hibernate, EclipseLink) write SQL for you; MyBatis, jOOQ, and JdbcClient let you control the SQL.
- Default for Spring Boot: Spring Data JPA. Need SQL control: MyBatis or jOOQ.
What's next?
Pick the web layer to go with your data layer in Java web frameworks, or turn your data into PDFs and dashboards with Java reporting frameworks.
Frequently Asked Questions
What is a persistence framework in Java?
A persistence framework is a library that saves Java objects to a database and loads them back, so you don't write all the low-level JDBC code yourself. Some, like Hibernate, map objects to tables automatically (ORM); others, like MyBatis and jOOQ, help you write and run SQL more safely.
What is the difference between JPA and Hibernate?
JPA (now called Jakarta Persistence) is a specification: a standard set of annotations and interfaces like @Entity and EntityManager. Hibernate is an implementation of that specification, the actual library that does the work. You code against JPA, and Hibernate runs it underneath.
Is Spring Data JPA the same as Hibernate?
No. Spring Data JPA sits on top of JPA and removes repetitive code: you declare a repository interface and Spring writes the queries for you. Underneath, it still needs a JPA implementation, which is Hibernate by default in Spring Boot.
Which Java persistence framework is best?
For most Spring Boot applications, Spring Data JPA with Hibernate is the standard choice. If you want full control over SQL, choose MyBatis or jOOQ. For simple data access without an ORM, Spring's JdbcClient is lightweight and easy to understand.
Should I use an ORM or write SQL myself?
An ORM saves a lot of code for typical create-read-update-delete work and keeps your model object-oriented. Hand-written SQL gives predictable performance and full use of database features. Many teams mix both: an ORM for everyday work and SQL for complex reports.