JPA/Hibernate-Muster
Verwenden für Datenmodellierung, Repositories und Leistungsoptimierung in Spring Boot.
Wann aktivieren
- Entwerfen von JPA-Entitäten und Tabellenmappings
- Definieren von Beziehungen (@OneToMany, @ManyToOne, @ManyToMany)
- Optimieren von Abfragen (N+1-Prävention, Fetch-Strategien, Projektionen)
- Konfigurieren von Transaktionen, Auditing oder Soft Deletes
- Einrichten von Paginierung, Sortierung oder benutzerdefinierten Repository-Methoden
- Optimieren von Connection Pooling (HikariCP) oder Second-Level-Caching
Entitätsdesign
@Entity
@Table(name = "markets", indexes = {
@Index(name = "idx_markets_slug", columnList = "slug", unique = true)
})
@EntityListeners(AuditingEntityListener.class)
public class MarketEntity {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 200)
private String name;
@Column(nullable = false, unique = true, length = 120)
private String slug;
@Enumerated(EnumType.STRING)
private MarketStatus status = MarketStatus.ACTIVE;
@CreatedDate private Instant createdAt;
@LastModifiedDate private Instant updatedAt;
}
Auditing aktivieren:
@Configuration
@EnableJpaAuditing
class JpaConfig {}
Beziehungen und N+1-Prävention
@OneToMany(mappedBy = "market", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PositionEntity> positions = new ArrayList<>();
- Standardmäßig Lazy Loading verwenden;
JOIN FETCHin Abfragen einsetzen, wenn nötig EAGERbei Sammlungen vermeiden; DTO-Projektionen für Lesevorgänge nutzen
@Query("select m from MarketEntity m left join fetch m.positions where m.id = :id")
Optional<MarketEntity> findWithPositions(@Param("id") Long id);
Repository-Muster
public interface MarketRepository extends JpaRepository<MarketEntity, Long> {
Optional<MarketEntity> findBySlug(String slug);
@Query("select m from MarketEntity m where m.status = :status")
Page<MarketEntity> findByStatus(@Param("status") MarketStatus status, Pageable pageable);
}
- Projektionen für schlanke Abfragen verwenden:
public interface MarketSummary {
Long getId();
String getName();
MarketStatus getStatus();
}
Page<MarketSummary> findAllBy(Pageable pageable);
Transaktionen
- Service-Methoden mit
@Transactionalannotieren @Transactional(readOnly = true)für Lesevorgänge zur Optimierung verwenden- Ausbreitungsverhalten sorgfältig wählen; langlaufende Transaktionen vermeiden
@Transactional
public Market updateStatus(Long id, MarketStatus status) {
MarketEntity entity = repo.findById(id)
.orElseThrow(() -> new EntityNotFoundException("Markt"));
entity.setStatus(status);
return Market.from(entity);
}
Paginierung
PageRequest page = PageRequest.of(pageNumber, pageSize, Sort.by("createdAt").descending());
Page<MarketEntity> markets = repo.findByStatus(MarketStatus.ACTIVE, page);
Für cursor-basierte Paginierung id > :lastId in JPQL mit Sortierung einfügen.
Indizierung und Leistung
- Indizes für häufige Filter hinzufügen (
status,slug, Fremdschlüssel) - Zusammengesetzte Indizes passend zu Abfragemustern verwenden (
status, created_at) select *vermeiden; nur benötigte Spalten projizieren- Batch-Schreibvorgänge mit
saveAllundhibernate.jdbc.batch_sizedurchführen
Connection Pooling (HikariCP)
Empfohlene Eigenschaften:
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.validation-timeout=5000
Für PostgreSQL LOB-Handling hinzufügen:
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
Caching
- First-Level-Cache ist pro EntityManager vorhanden; Entitäten nicht über Transaktionen hinweg halten
- Für leseintensive Entitäten Second-Level-Cache vorsichtig in Betracht ziehen; Bereinigungsstrategie validieren
Migrationen
- Flyway oder Liquibase verwenden; niemals auf Hibernate Auto DDL in Produktion setzen
- Migrationen idempotent und additiv halten; Spalten nicht ohne Plan löschen
Testen des Datenzugriffs
@DataJpaTestmit Testcontainers bevorzugen, um die Produktion abzubilden- SQL-Effizienz mit Logs überprüfen:
logging.level.org.hibernate.SQL=DEBUGundlogging.level.org.hibernate.orm.jdbc.bind=TRACEfür Parameterwerte setzen
Denken Sie daran: Entitäten schlank halten, Abfragen zielgerichtet und Transaktionen kurz. N+1 mit Fetch-Strategien und Projektionen verhindern und für Ihre Lese-/Schreibpfade indizieren.