{"id":654,"date":"2026-03-25T22:06:10","date_gmt":"2026-03-25T21:06:10","guid":{"rendered":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/?p=654"},"modified":"2026-06-16T22:33:08","modified_gmt":"2026-06-16T21:33:08","slug":"hibernate-performance-optimieren-n1-batch-fetching-und-caching","status":"publish","type":"post","link":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/?p=654","title":{"rendered":"Hibernate Performance optimieren \u2013 N+1, Batch-Fetching und Caching"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Hibernate ist das popul\u00e4rste ORM-Framework im Java-\u00d6kosystem und bildet die Grundlage von Spring Data JPA. Es macht Datenbankzugriffe fast unsichtbar \u2013 aber genau diese Transparenz kann zu gravierenden Performance-Problemen f\u00fchren, wenn man die Mechanik unter der Oberfl\u00e4che nicht versteht. Das N+1-Problem ist der Klassiker unter den Hibernate-Fallstricken.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Das N+1-Problem verstehen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Angenommen, ein Webshop hat&nbsp;<code>Bestellung<\/code>-Entit\u00e4ten, die \u00fcber&nbsp;<code>@OneToMany<\/code>&nbsp;mit&nbsp;<code>BestellPosition<\/code>&nbsp;verkn\u00fcpft sind:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@Entity<\/span>\npublic class Bestellung {\n    <span class=\"hljs-keyword\">@Id<\/span> private Long id;\n    <span class=\"hljs-keyword\">@OneToMany<\/span>(mappedBy = <span class=\"hljs-string\">\"bestellung\"<\/span>, fetch = FetchType.LAZY)\n    private List&lt;BestellPosition&gt; positionen;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Ein simpler Aufruf wie&nbsp;<code>bestellungRepository.findAll()<\/code>&nbsp;erzeugt folgende SQL-Queries:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span>-- 1 Query f\u00fcr alle Bestellungen<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\nSELECT * FROM bestellung;  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span>-- liefert 100 Zeilen<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span>-- 100 weitere Queries \u2013 eine pro Bestellung!<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\nSELECT * FROM bestell_position WHERE bestellung_id = 1;\nSELECT * FROM bestell_position WHERE bestellung_id = 2;\n...\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">1 + 100 = 101 Queries f\u00fcr eine scheinbar einfache Operation. Bei 1.000 Bestellungen sind es 1.001 Queries. Das ist das&nbsp;<strong>N+1-Problem<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">L\u00f6sung 1: JOIN FETCH<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Die einfachste L\u00f6sung ist ein explizites&nbsp;<code>JOIN FETCH<\/code>&nbsp;im JPQL-Query:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">@Query(<span class=\"hljs-string\">\"SELECT DISTINCT b FROM Bestellung b \"<\/span> +\n       <span class=\"hljs-string\">\"JOIN FETCH b.positionen\"<\/span>)\n<span class=\"hljs-keyword\">List<\/span>&lt;Bestellung&gt; findAllMitPositionen();\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Das erzeugt exakt eine SQL-Query mit einem JOIN.&nbsp;<code>DISTINCT<\/code>&nbsp;verhindert Duplikate durch die JOIN-Vervielfachung. Die Daten sind nach einer Query vollst\u00e4ndig geladen \u2013 N+1 gel\u00f6st.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">L\u00f6sung 2: Entity Graphs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Statt Query-Annotationen im Repository kann man Ladeverhalten deklarativ \u00fcber&nbsp;<code>@EntityGraph<\/code>&nbsp;steuern:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">@Entity\n@NamedEntityGraph(\n    name = <span class=\"hljs-string\">\"Bestellung.mitPositionen\"<\/span>,\n    attributeNodes = @NamedAttributeNode(<span class=\"hljs-string\">\"positionen\"<\/span>))\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Bestellung<\/span> <\/span>{ ... }\n\n&lt;em&gt;<span class=\"hljs-comment\">\/\/ Im Repository:&lt;\/em&gt;<\/span>\n@EntityGraph(<span class=\"hljs-string\">\"Bestellung.mitPositionen\"<\/span>)\n<span class=\"hljs-keyword\">List<\/span>&lt;Bestellung&gt; findAll();\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Entity Graphs sind besonders n\u00fctzlich, wenn verschiedene Aufrufer unterschiedliche Ladeverhalten ben\u00f6tigen \u2013 der Controller f\u00fcr die Listenansicht l\u00e4dt nur die Bestellungen, der Detail-Controller l\u00e4dt zus\u00e4tzlich die Positionen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">L\u00f6sung 3: Batch-Fetching mit @BatchSize<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>JOIN FETCH<\/code>&nbsp;ist m\u00e4chtig, aber nicht immer anwendbar \u2013 mehrere&nbsp;<code>JOIN FETCH<\/code>-Klauseln auf Collections f\u00fchren zu kartesischen Produkten und multiplizieren die Datenmenge. Hier hilft&nbsp;<code>@BatchSize<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">@Entity\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Bestellung<\/span> <\/span>{\n    @OneToMany(mappedBy = <span class=\"hljs-string\">\"bestellung\"<\/span>)\n    @BatchSize(size = <span class=\"hljs-number\">50<\/span>)\n    <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">List<\/span>&lt;BestellPosition&gt; positionen;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Statt 100 einzelner Queries fasst Hibernate die Lazy-Loads in Batches von 50 zusammen \u2013 aus 100 Queries werden 2. Der Abruf bleibt lazy, aber die Query-Anzahl sinkt drastisch.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">N+1 erkennen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Hibernate bietet eingebaute Metriken, um das N+1-Problem sichtbar zu machen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">spring.jpa.properties.hibernate.generate_statistics=<span class=\"hljs-literal\">true<\/span>\nlogging.level.org.hibernate.stat=DEBUG\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Der Log zeigt dann die Session-Statistiken an:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Session Metrics {\n    101 JDBC statements\n    100 collections loaded lazily\n}\n<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Hier erkennt man sofort: 100 Collections wurden einzeln nachgeladen \u2013 der Fall f\u00fcr JOIN FETCH oder @BatchSize.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Second-Level-Cache<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Der Second-Level-Cache (L2-Cache) speichert Entit\u00e4ten \u00fcber Sitzungen hinweg \u2013 anders als der automatische First-Level-Cache, der nur f\u00fcr eine Session \/ einen Request gilt:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">@Entity\n@Cacheable\n@org.hibernate.annotations.Cache(\n    usage = CacheConcurrencyStrategy.READ_WRITE)\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Produkt<\/span> <\/span>{\n    @Id <span class=\"hljs-keyword\">private<\/span> Long id;\n    <span class=\"hljs-keyword\">private<\/span> String name;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Aktivierung in der Konfiguration:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">spring.jpa.properties.hibernate.cache.use_second_level_cache=<span class=\"hljs-literal\">true<\/span>\nspring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.jcache.JCacheRegionFactory\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Der L2-Cache reduziert Datenbankzugriffe f\u00fcr h\u00e4ufig gelesene Referenzdaten (Produktkatalog, Stammdaten) erheblich. F\u00fcr den Query-Cache \u2013 der sich auf ganze JPQL-Queries bezieht \u2013 ist Vorsicht geboten: Er wird bei jeder schreibenden Operation invalidiert und kann in transaktionalen Systemen mehr schaden als n\u00fctzen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">DTO-Projektionen statt Entities<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht jeder Lesezugriff ben\u00f6tigt vollst\u00e4ndige Entities. Oft interessieren nur wenige Felder \u2013 trotzdem l\u00e4dt Hibernate standardm\u00e4\u00dfig die komplette Entit\u00e4t mit allen @OneToMany-Beziehungen. Eine DTO-Projektion l\u00e4dt nur die ben\u00f6tigten Spalten:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-keyword\">public<\/span> record BestellungDto(Long id, String kunde, BigDecimal summe) {}\n\n@Query(<span class=\"hljs-string\">\"SELECT new com.example.dto.BestellungDto(\"<\/span> +\n       <span class=\"hljs-string\">\"b.id, b.kunde, b.summe) \"<\/span> +\n       <span class=\"hljs-string\">\"FROM Bestellung b WHERE b.status = :status\"<\/span>)\n<span class=\"hljs-keyword\">List<\/span>&lt;BestellungDto&gt; findOffeneBestellungen(Status status);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Hibernate erzeugt daf\u00fcr ein&nbsp;<code>SELECT id, kunde, summe FROM ...<\/code>&nbsp;\u2013 ohne JOINs auf die Positionen-Collection. Ein typischer Performance-Gewinn von 90 % und mehr gegen\u00fcber dem Laden der gesamten Entity.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Read-Only-Sessions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn Daten nur gelesen und nicht ver\u00e4ndert werden, kann das Dirty-Checking deaktiviert werden \u2013 Hibernate muss \u00c4nderungen dann nicht \u00fcberwachen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">@Transactional(readOnly = <span class=\"hljs-keyword\">true<\/span>)\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">List<\/span>&lt;Bestellung&gt; findAlle() {\n    <span class=\"hljs-keyword\">return<\/span> repository.findAll();\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Innerhalb einer&nbsp;<code>@Transactional(readOnly = true)<\/code>-Methode \u00fcberspringt Hibernate das Flushing und das Dirty-Checking. Der Performance-Effekt ist besonders bei gro\u00dfen Result-Sets sp\u00fcrbar.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Optimistic Locking<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr schreibende Zugriffe mit hoher Parallelit\u00e4t bietet Hibernate Optimistic Locking mittels&nbsp;<code>@Version<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@Entity<\/span>\npublic class Produkt {\n    <span class=\"hljs-keyword\">@Id<\/span> private Long id;\n    <span class=\"hljs-selector-tag\">private<\/span> <span class=\"hljs-selector-tag\">int<\/span> <span class=\"hljs-selector-tag\">lagerbestand<\/span>;\n\n    <span class=\"hljs-keyword\">@Version<\/span>\n    private Long version;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Hibernate pr\u00fcft beim UPDATE, dass die Version seit dem Laden unver\u00e4ndert ist. Bei Kollisionen wirft es eine&nbsp;<code>OptimisticLockException<\/code>, die der Aufrufer mit einem Retry beantworten kann. Das vermeidet teure Datenbank-Sperren und erh\u00f6ht den Durchsatz.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Praktische Checkliste<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Problem<\/th><th class=\"has-text-align-left\" data-align=\"left\">L\u00f6sung<\/th><\/tr><\/thead><tbody><tr><td>N+1 auf ToOne-Relation<\/td><td><code>JOIN FETCH<\/code><\/td><\/tr><tr><td>N+1 auf ToMany-Relation<\/td><td><code>@BatchSize<\/code>&nbsp;oder&nbsp;<code>JOIN FETCH<\/code><\/td><\/tr><tr><td>Mehrere Collection-Fetches<\/td><td><code>@BatchSize<\/code>&nbsp;statt&nbsp;<code>JOIN FETCH<\/code><\/td><\/tr><tr><td>H\u00e4ufig gelesene Stammdaten<\/td><td>Second-Level-Cache<\/td><\/tr><tr><td>Unn\u00f6tig viele Spalten<\/td><td>DTO-Projektion via&nbsp;<code>new com.example.Dto(...)<\/code>&nbsp;im JPQL<\/td><\/tr><tr><td>Nur-Lese-Zugriffe<\/td><td><code>@Transactional(readOnly = true)<\/code><\/td><\/tr><tr><td>Parallele Schreibzugriffe<\/td><td><code>@Version<\/code>&nbsp;f\u00fcr Optimistic Locking<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Jakarta Persistence: Der Wechsel von javax zu jakarta<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Seit Hibernate 6.0 (Mai 2022) verwendet Hibernate&nbsp;<strong>Jakarta Persistence 3.0+<\/strong>&nbsp;statt der bisherigen Java Persistence API (JPA). Das bedeutet: Aus&nbsp;<code>javax.persistence.*<\/code>&nbsp;wurde&nbsp;<code>jakarta.persistence.*<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">&lt;em&gt;<span class=\"hljs-comment\">\/\/ Vor Hibernate 6.0 (JPA 2.x)&lt;\/em&gt;<\/span>\n<span class=\"hljs-keyword\">import<\/span> javax.persistence.Entity;\n<span class=\"hljs-keyword\">import<\/span> javax.persistence.Id;\n<span class=\"hljs-keyword\">import<\/span> javax.persistence.OneToMany;\n\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span>\/\/ Ab Hibernate 6.0 (Jakarta Persistence 3.x)<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span><\/span>\n<span class=\"hljs-keyword\">import<\/span> jakarta.persistence.Entity;\n<span class=\"hljs-keyword\">import<\/span> jakarta.persistence.Id;\n<span class=\"hljs-keyword\">import<\/span> jakarta.persistence.OneToMany;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>Aktuelle Versionen (Stand Juni 2026):<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Hibernate 7.4 (latest stable) \u2192 Jakarta Persistence 3.2<\/li>\n\n\n\n<li>Hibernate 6.6 (Spring Boot 3.4-3.5) \u2192 Jakarta Persistence 3.1<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr bestehende Anwendungen bedeutet die Migration zu Hibernate 6+ prim\u00e4r ein Package-Rename. Die API selbst bleibt weitgehend kompatibel \u2013 alle in diesem Artikel beschriebenen Annotationen (<code>@Entity<\/code>,&nbsp;<code>@OneToMany<\/code>,&nbsp;<code>@Version<\/code>,&nbsp;<code>@Cacheable<\/code>, etc.) funktionieren identisch, nur unter dem neuen Package-Namen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Fazit<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Hibernate ist ein m\u00e4chtiges Werkzeug, aber ohne Verst\u00e4ndnis der Lade-Strategien eine Quelle subtiler Performance-Probleme. Das N+1-Problem l\u00e4sst sich mit&nbsp;<code>JOIN FETCH<\/code>,&nbsp;<code>@EntityGraph<\/code>&nbsp;oder&nbsp;<code>@BatchSize<\/code>&nbsp;zuverl\u00e4ssig beheben. DTO-Projektionen sparen unn\u00f6tige Datenbanklast, Read-Only-Sessions vermeiden Dirty-Checking, und der Second-Level-Cache entlastet die Datenbank bei wiederholten Lesezugriffen. Die goldene Regel: Niemals eine Hibernate-Anwendung in Produktion gehen lassen, ohne die SQL-Statistiken gepr\u00fcft zu haben.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hibernate ist das popul\u00e4rste ORM-Framework im Java-\u00d6kosystem und bildet die Grundlage von Spring Data JPA. Es macht Datenbankzugriffe fast unsichtbar \u2013 aber genau diese Transparenz kann zu gravierenden Performance-Problemen f\u00fchren, wenn man die Mechanik unter der Oberfl\u00e4che nicht versteht. Das N+1-Problem ist der Klassiker unter den Hibernate-Fallstricken. Das N+1-Problem verstehen Angenommen, ein Webshop hat&nbsp;Bestellung-Entit\u00e4ten, die [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,5],"tags":[],"class_list":["post-654","post","type-post","status-publish","format-standard","hentry","category-plain_java","category-spring"],"_links":{"self":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/654","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=654"}],"version-history":[{"count":1,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/654\/revisions"}],"predecessor-version":[{"id":655,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/654\/revisions\/655"}],"wp:attachment":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=654"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=654"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=654"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}