ORM & Persistence
JPA and Hibernate, entity mappings, JPQL, the N+1 problem, and Spring Data JPA.
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.
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); // DELETENo 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
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.