⌂ Index
Kapitel 06 · Data

Spring Data JPA, Hibernate und Transaktionen

Typ: FrameworkVersion 2 ausführlich

Spring Data JPA erleichtert JPA-basierte Repositories und integriert sich mit EntityManager, Hibernate, Transaktionen und Spring Data Repository-Konventionen.

Fachliche Einordnung

JPA ist stark bei relationalen Domänenmodellen, aber kritisch bei Lazy Loading, N+1 Queries, Transaktionsgrenzen und Entity-Lifecycle. Enterprise-Code braucht klare Regeln.

Enterprise-Merksatz: Spring Data JPA erleichtert JPA-basierte Repositories und integriert sich mit EntityManager, Hibernate, Transaktionen und Spring Data Repository-Konventionen.

Technische Darstellung

Use Case @Transactional JPA Repository EntityManager Hibernate Relational DB
Kernkonzepte
  • Entity, Aggregate Root, Repository und Unit of Work.
  • @Transactional als Service-Grenze.
  • Optimistic Locking mit @Version.
  • Fetch Join, EntityGraph und DTO Projection gegen N+1.
  • Migrationswerkzeuge wie Flyway/Liquibase ergänzen JPA.
Wann einsetzen?
  • Du hast relationales Datenmodell und komplexe fachliche Aggregatregeln.
  • Du brauchst Transaktionen über mehrere Tabellen.
  • Du willst CRUD plus fachliche Queries effizient umsetzen.
Typische Fehler und Risiken
  • Open Session in View verdeckt falsche Transaktionsgrenzen.
  • Entities direkt als REST DTOs verwenden.
  • CascadeType.ALL überall setzen.
  • Bidirektionale Beziehungen ohne klare Aggregate.
Legacy- und Modernisierungssicht

CMP Entity Beans oder JDBC-DAOs werden nicht 1:1 nach JPA kopiert, sondern fachlich zu Aggregaten und Read Models neu geschnitten.

Ausführliches Beispiel

Das Beispiel zeigt bewusst nicht nur Annotationen, sondern auch die Verantwortung der Schicht. In echten Projekten sollte der technische Spring-Code an Adapter- oder Konfigurationsrändern bleiben, während die Fachlogik testbar und möglichst frameworkarm bleibt.

@Entity
@Table(name = "orders")
class OrderEntity {
    @Id UUID id;
    @Version long version;
    @Enumerated(EnumType.STRING) OrderStatus status;
    BigDecimal totalAmount;

    protected OrderEntity() {}

    void confirmPayment() {
        if (status != OrderStatus.RESERVED) throw new IllegalStateException("not reserved");
        status = OrderStatus.PAID;
    }
}

interface JpaOrderRepository extends JpaRepository<OrderEntity, UUID> {
    @EntityGraph(attributePaths = "lines")
    Optional<OrderEntity> findWithLinesById(UUID id);
}

@Service
class ConfirmPaymentService {
    @Transactional
    public void confirm(UUID orderId) {
        OrderEntity order = repository.findWithLinesById(orderId).orElseThrow();
        order.confirmPayment(); // Dirty Checking schreibt beim Commit.
    }
}
Checkliste für Reviews
  • Ist die Verantwortung des Bausteins klar: Framework steuert Lebenszyklus, Library wird gezielt benutzt?
  • Ist die Fachlogik außerhalb von Controller, Listener, Repository-Implementierung oder Konfiguration?
  • Sind Fehlerfälle, Timeouts, Security, Monitoring und Tests sichtbar modelliert?
  • Gibt es klare Grenzen zwischen DTO, Domäne, Persistence und Infrastruktur?