{"id":675,"date":"2026-05-26T00:30:43","date_gmt":"2026-05-25T23:30:43","guid":{"rendered":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/?p=675"},"modified":"2026-06-17T00:31:56","modified_gmt":"2026-06-16T23:31:56","slug":"jooq-typsicheres-sql-und-datenbankzugriffe-jenseits-von-jpa","status":"publish","type":"post","link":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/?p=675","title":{"rendered":"jOOQ \u2014 Typsicheres SQL und Datenbankzugriffe jenseits von JPA"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">JPA und Hibernate sind m\u00e4chtige Werkzeuge f\u00fcr objektrelationales Mapping \u2014 aber sie sto\u00dfen bei komplexen SQL-Abfragen schnell an ihre Grenzen. jOOQ (Java Object Oriented Querying) geht einen anderen Weg: Es bildet SQL eins zu eins als typsichere Java-DSL ab. Statt SQL in Strings zu verstecken wird jede Tabelle, jede Spalte und jede Datenbankfunktion zu einer Java-Klasse.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Warum jOOQ?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Stell dir folgende JPA-Abfrage vor:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">&lt;em&gt;<span class=\"hljs-comment\">\/\/ JPA: JPQL als String \u2014 keine Typsicherheit durch den Compiler&lt;\/em&gt;<\/span>\n<span class=\"hljs-built_in\">String<\/span> jpql = <span class=\"hljs-string\">\"SELECT b FROM Buch b WHERE b.preis &gt; :minPreis\"<\/span>;\nList&lt;Buch&gt; buecher = entityManager.createQuery(jpql, Buch.class)\n    .setParameter(<span class=\"hljs-string\">\"minPreis\"<\/span>, <span class=\"hljs-number\">19.99<\/span>)\n    .getResultList();\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><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\">Jeder Tippfehler im String f\u00e4llt erst zur Laufzeit auf. Mit jOOQ sieht dieselbe Abfrage so aus:<\/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>\/\/ jOOQ: Typsicheres SQL \u2014 der Compiler pr\u00fcft Tabellen- und Spaltennamen<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\nResult<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Record<\/span>&gt;<\/span> result = dsl.select()\n    .from(BUCH)\n    .where(BUCH.PREIS.gt(BigDecimal.valueOf(19.99)))\n    .fetch();\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\">Der Unterschied:&nbsp;<code>BUCH<\/code>&nbsp;und&nbsp;<code>PREIS<\/code>&nbsp;sind generierte Klassen, keine Strings. Vertippt man sich, meckert der Compiler sofort.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Codegenerierung aus dem Datenbankschema<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Das Herzst\u00fcck von jOOQ ist der Code-Generator. Er liest das Schema der Datenbank aus und erzeugt Java-Klassen f\u00fcr Tabellen, Views, Sequenzen, Stored Procedures und sogar ENUM-Typen.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" 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><span class=\"hljs-comment\">&lt;!-- pom.xml \u2014 jOOQ Code-Generator via Maven --&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">plugin<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.jooq<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>jooq-codegen-maven<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>3.21.6<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">executions<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">execution<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">goals<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">goal<\/span>&gt;<\/span>generate<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">goal<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">goals<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">execution<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">executions<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">configuration<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">jdbc<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">driver<\/span>&gt;<\/span>org.postgresql.Driver<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">driver<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">url<\/span>&gt;<\/span>jdbc:postgresql:\/\/localhost:5432\/bibliothek<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">url<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">user<\/span>&gt;<\/span>postgres<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">user<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">jdbc<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">generator<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">database<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">name<\/span>&gt;<\/span>org.jooq.meta.postgres.PostgresDatabase<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">name<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">includes<\/span>&gt;<\/span>.*<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">includes<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">database<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">target<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">packageName<\/span>&gt;<\/span>com.example.jooq<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">packageName<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">target<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">generator<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">configuration<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">plugin<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><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\">Nach&nbsp;<code>mvn generate-sources<\/code>&nbsp;liegen im Target-Package fertige Klassen wie&nbsp;<code>com.example.jooq.Tables.BUCH<\/code>&nbsp;und&nbsp;<code>com.example.jooq.tables.records.BuchRecord<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Komplexe Queries ohne SQL-Strings<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Das volle Potenzial von jOOQ zeigt sich bei komplexen Abfragen mit Joins, CTEs und Window Functions:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">&lt;em&gt;<span class=\"hljs-comment\">\/\/ Alle Autoren mit mehr als 3 B\u00fcchern und deren letztem Buchtitel&lt;\/em&gt;<\/span>\n<span class=\"hljs-keyword\">var<\/span> result = dsl.select(\n        AUTOR.NAME,\n        count().as(<span class=\"hljs-string\">\"anzahl\"<\/span>),\n        max(BUCH.TITEL).as(<span class=\"hljs-string\">\"letztes_buch\"<\/span>)\n    )\n    .from(AUTOR)\n    .innerJoin(BUCH).on(BUCH.AUTOR_ID.eq(AUTOR.ID))\n    .groupBy(AUTOR.NAME)\n    .having(count().gt(<span class=\"hljs-number\">3<\/span>))\n    .orderBy(count().desc())\n    .fetch();\n\n<span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">var<\/span> record : result) {\n    System.out.printf(<span class=\"hljs-string\">\"%s: %d B\u00fccher, letztes: %s%n\"<\/span>,\n        record.get(AUTOR.NAME),\n        record.get(<span class=\"hljs-string\">\"anzahl\"<\/span>, Integer.class),\n        record.get(<span class=\"hljs-string\">\"letztes_buch\"<\/span>, <span class=\"hljs-built_in\">String<\/span>.class));\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><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<h2 class=\"wp-block-heading\">Transaktionen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">jOOQ klinkt sich nahtlos in das Transaktionsmanagement deiner Anwendung ein \u2014 egal ob \u00fcber JDBC, Spring&nbsp;<code>@Transactional<\/code>&nbsp;oder programmatisch:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">&lt;em&gt;<span class=\"hljs-comment\">\/\/ Programmatische Transaktion \u00fcber jOOQ&lt;\/em&gt;<\/span>\ndsl.transaction(configuration -&gt; {\n    DSLContext ctx = DSL.using(configuration);\n\n    ctx.insertInto(AUTOR)\n       .set(AUTOR.ID, <span class=\"hljs-number\">42<\/span>)\n       .set(AUTOR.NAME, <span class=\"hljs-string\">\"Orwell\"<\/span>)\n       .execute();\n\n    ctx.insertInto(BUCH)\n       .set(BUCH.ID, <span class=\"hljs-number\">1<\/span>)\n       .set(BUCH.AUTOR_ID, <span class=\"hljs-number\">42<\/span>)\n       .set(BUCH.TITEL, <span class=\"hljs-string\">\"1984\"<\/span>)\n       .execute();\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span>\/\/ Erfolgreich \u2014 Commit<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span><\/span>\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span>\/\/ Bei Exception \u2014 automatischer Rollback<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span><\/span>\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\">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\">In Spring-Umgebungen nutzt du einfach Spring-<code>TransactionTemplate<\/code>&nbsp;oder&nbsp;<code>@Transactional<\/code>&nbsp;\u2014 jOOQ verwendet denselben zugrundeliegenden&nbsp;<code>DataSource<\/code>-Connection-Pool und partizipiert automatisch an der laufenden Transaktion.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Dynamische Queries<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ein h\u00e4ufiges Problem sind dynamische Filterkriterien (\u201ezeige B\u00fccher, optional gefiltert nach Autor und\/oder Preis\u201c). Mit jOOQ ist das elegant l\u00f6sbar:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">List<\/span>&lt;BuchRecord&gt; filterBuecher(String author, BigDecimal minPreis) {\n    <span class=\"hljs-keyword\">var<\/span> condition = noCondition();\n\n    <span class=\"hljs-keyword\">if<\/span> (author != <span class=\"hljs-keyword\">null<\/span>) {\n        condition = condition.<span class=\"hljs-keyword\">and<\/span>(AUTOR.NAME.eq(author));\n    }\n    <span class=\"hljs-keyword\">if<\/span> (minPreis != <span class=\"hljs-keyword\">null<\/span>) {\n        condition = condition.<span class=\"hljs-keyword\">and<\/span>(BUCH.PREIS.gt(minPreis));\n    }\n\n    <span class=\"hljs-keyword\">return<\/span> dsl.selectFrom(BUCH)\n        .innerJoin(AUTOR).on(BUCH.AUTOR_ID.eq(AUTOR.ID))\n        .where(condition)\n        .fetch();\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><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\">Die&nbsp;<code>noCondition()<\/code>&nbsp;ist eine neutrale Bedingung, die \u2014 kombiniert mit&nbsp;<code>.and()<\/code>&nbsp;\u2014 ein beliebig komplexes dynamisches Where erlaubt.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">jOOQ vs. JPA \u2014 wann was verwenden?<\/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\">Kriterium<\/th><th class=\"has-text-align-left\" data-align=\"left\">jOOQ<\/th><th class=\"has-text-align-left\" data-align=\"left\">JPA \/ Hibernate<\/th><\/tr><\/thead><tbody><tr><td>SQL-N\u00e4he<\/td><td>Eins-zu-eins-Abbildung<\/td><td>Abstraktion (JPQL)<\/td><\/tr><tr><td>Typsicherheit<\/td><td>Vollst\u00e4ndig<\/td><td>Nur teilweise<\/td><\/tr><tr><td>Komplexe SQL<\/td><td>Hervorragend<\/td><td>M\u00fchsam<\/td><\/tr><tr><td>CRUD-Operationen<\/td><td>M\u00f6glich, aber weniger komfortabel<\/td><td>Sehr komfortabel (Spring Data)<\/td><\/tr><tr><td>Lernkurve<\/td><td>SQL-Kenntnisse n\u00f6tig<\/td><td>ORM-Konzepte lernen<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Ein sinnvoller Ansatz: jOOQ f\u00fcr Reporting, Analytics und komplexe Abfragen einsetzen; JPA\/Spring Data f\u00fcr einfache CRUD-Operationen verwenden. Beide Bibliotheken lassen sich innerhalb derselben Transaktion kombinieren.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Moderne Features, die den Unterschied machen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Neben der typsicheren DSL bringt jOOQ einige Features mit, die \u00fcber klassisches JDBC weit hinausgehen und selbst in vielen ORMs fehlen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">MULTISET \u2014 Nested Collections ohne N+1-Problem<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Das seit jOOQ 3.15 verf\u00fcgbare&nbsp;<code>MULTISET<\/code>-Konstrukt erlaubt es, 1:n-Beziehungen in einer einzigen Query zu laden \u2014 ganz ohne Join-Fetches oder nachgelagerte Schleifen:<\/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\">&lt;em&gt;<span class=\"hljs-comment\">\/\/ Alle Autoren mit ihren B\u00fcchern in EINER Abfrage&lt;\/em&gt;<\/span>\n<span class=\"hljs-keyword\">var<\/span> result = dsl.select(\n        AUTOR.NAME,\n        multiset(\n            select(BUCH.TITEL, BUCH.PREIS)\n            .from(BUCH)\n            .where(BUCH.AUTOR_ID.eq(AUTOR.ID))\n        ).<span class=\"hljs-keyword\">as<\/span>(<span class=\"hljs-string\">\"buecher\"<\/span>)\n    )\n    .from(AUTOR)\n    .fetch();\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\">Das Ergebnis enth\u00e4lt pro Autor eine geschachtelte&nbsp;<code>Result&lt;Record&gt;<\/code>-Collection \u2014 jOOQ emuliert&nbsp;<code>MULTISET<\/code>&nbsp;transparent \u00fcber SQL\/JSON oder SQL\/XML, falls die Datenbank kein natives&nbsp;<code>MULTISET<\/code>&nbsp;unterst\u00fctzt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Reactive und R2DBC-Unterst\u00fctzung<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">jOOQ unterst\u00fctzt reaktive Programmierung \u00fcber R2DBC. Statt blockierendem JDBC l\u00e4sst sich eine&nbsp;<code>io.r2dbc.spi.ConnectionFactory<\/code>&nbsp;an den&nbsp;<code>DSLContext<\/code>&nbsp;\u00fcbergeben:<\/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\"><span class=\"hljs-keyword\">var<\/span> publisher = dsl.selectFrom(BUCH)\n    .where(BUCH.PREIS.gt(BigDecimal.valueOf(<span class=\"hljs-number\">20<\/span>)))\n    .fetchAsync();  <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span>\/\/ Non-blocking via R2DBC<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span><\/span>\n\nFlux.from(publisher)\n    .map(r -&gt; r.into(BuchRecord.class))\n    .subscribe(buch -&gt; log.info(<span class=\"hljs-string\">\"Buch: {}\"<\/span>, buch.getTitel()));\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\">Das funktioniert out of the box mit Project Reactor, RxJava oder JDK 9&nbsp;<code>Flow.Publisher<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Kotlin-Support<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">jOOQ bietet erstklassigen Kotlin-Support mit Coroutine-Integration, Extension Functions und speziellen&nbsp;<code>MULTISET<\/code>-Collectors f\u00fcr idiomatischen Kotlin-Code. Die Open-Source-Edition wird mit Java 21, Scala 3.5 und Kotlin 2 ausgeliefert.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Fazit<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">jOOQ bringt die Typsicherheit und den Komfort moderner Java-Entwicklung in die SQL-Welt. Es ersetzt JPA nicht, sondern erg\u00e4nzt es ideal dort, wo SQL-Kontrolle wichtiger ist als Objektabstraktion. Wer schon immer davon getr\u00e4umt hat, SQL in Java zu schreiben \u2014 ohne Strings, ohne Reflection, mit voller Compiler-Unterst\u00fctzung \u2014 findet in jOOQ das passende Werkzeug.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>JPA und Hibernate sind m\u00e4chtige Werkzeuge f\u00fcr objektrelationales Mapping \u2014 aber sie sto\u00dfen bei komplexen SQL-Abfragen schnell an ihre Grenzen. jOOQ (Java Object Oriented Querying) geht einen anderen Weg: Es bildet SQL eins zu eins als typsichere Java-DSL ab. Statt SQL in Strings zu verstecken wird jede Tabelle, jede Spalte und jede Datenbankfunktion zu einer [&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],"tags":[],"class_list":["post-675","post","type-post","status-publish","format-standard","hentry","category-plain_java"],"_links":{"self":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/675","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=675"}],"version-history":[{"count":1,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/675\/revisions"}],"predecessor-version":[{"id":676,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/675\/revisions\/676"}],"wp:attachment":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=675"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=675"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=675"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}