Loslegen
Javaneer
Zurück zur Stufe
Stufe 6·Professionelles Java

ORM & Persistence

JPA and Hibernate, entity mappings, JPQL, the N+1 problem, and Spring Data JPA.

20 Min. LesezeitExperte

Deutsche Übersetzung in Arbeit

Diese Lektion ist noch nicht ins Deutsche übersetzt und wird daher auf Englisch angezeigt. Der Rest der Seite ist vollständig lokalisiert.

Auf dieser Seite

Writing raw JDBC for every query is tedious and repetitive. An ORM (Object-Relational Mapping) tool bridges the gap between your Java objects and database tables, letting you work with objects instead of SQL most of the time. In Java, the standard is JPA, most commonly implemented by Hibernate.

The object-relational mismatch

Java thinks in objects with references; databases think in tables with rows and foreign keys. Translating between them by hand is repetitive and error-prone.

An ORM is like a translator between two languages

You speak Java (objects); the database speaks SQL (tables). An ORM is a skilled translator sitting between you: you say "save this Order object", and it produces the correct INSERT statements. You mostly think in objects and let the translator handle the SQL.

Entities: mapping objects to tables

With JPA, you annotate a class as an @Entity and it maps to a table:

import jakarta.persistence.*;

@Entity
@Table(name = "products")
class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private double price;

    // constructors, getters, setters...
}
  • @Entity - this class maps to a table.
  • @Id - the primary key.
  • @GeneratedValue - the database generates the id automatically.
  • Each field maps to a column (customizable with @Column).

Basic operations

JPA's EntityManager (or Spring Data, below) handles persistence:

Product p = new Product("Keyboard", 49.99);
entityManager.persist(p);                       // INSERT

Product found = entityManager.find(Product.class, 1L);   // SELECT by id
found.setPrice(39.99);                          // UPDATE on commit
entityManager.remove(found);                    // DELETE

No SQL written - the ORM generates it.

Relationships

JPA maps object relationships to foreign keys:

@Entity
class Order {
    @Id @GeneratedValue Long id;

    @OneToMany(mappedBy = "order")
    private List<OrderItem> items;    // one order has many items

    @ManyToOne
    private Customer customer;         // many orders belong to one customer
}

JPQL: querying with objects

For custom queries, JPQL looks like SQL but operates on entities and fields, not tables and columns:

List<Product> cheap = entityManager
    .createQuery("SELECT p FROM Product p WHERE p.price < :max", Product.class)
    .setParameter("max", 50.0)
    .getResultList();

The N+1 problem - the classic ORM trap

N+1 is like asking about each guest one by one

You fetch a list of 100 orders (1 query). Then, for each order, you access its customer - triggering a separate query per order (100 more). That's 101 queries where a single smart join would do. It's like inviting 100 guests, then phoning each individually to ask their name instead of checking one guest list.

Watch for N+1

The N+1 problem quietly destroys performance as data grows. Fix it by fetching related data together - a JOIN FETCH in JPQL, an entity graph, or batch fetching. Always check how many queries your ORM actually runs (enable SQL logging) - the convenience can hide expensive surprises.

Spring Data JPA - even less boilerplate

Spring Data JPA removes almost all remaining plumbing. Declare an interface, and Spring generates the implementation - even deriving queries from method names:

interface ProductRepository extends JpaRepository<Product, Long> {
    List<Product> findByPriceLessThan(double max);   // query generated from the name!
}
productRepository.save(product);
List<Product> cheap = productRepository.findByPriceLessThan(50.0);

Quick check

What is the 'N+1 problem' in ORM usage?

Key takeaways

  • An ORM maps Java objects to database tables, so you work with objects instead of raw SQL.
  • JPA is the standard; Hibernate is the common implementation.
  • @Entity/@Id/@GeneratedValue map a class to a table; annotations map relationships to foreign keys.
  • JPQL queries entities and fields (not tables); the EntityManager handles persist/find/remove.
  • Beware the N+1 problem - use JOIN FETCH or batching, and log SQL to see what really runs.
  • Spring Data JPA generates repository implementations, even deriving queries from method names.

Now let's tie it all together with the framework you'll use most: Spring Boot.