{"id":1033,"date":"2011-07-12T18:09:36","date_gmt":"2011-07-12T16:09:36","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/2011\/07\/gastbeitrag-sql-als-interne-dsl-in-java-mit-jooq\/"},"modified":"2011-07-12T18:43:04","modified_gmt":"2011-07-12T16:43:04","slug":"gastbeitrag-sql-als-interne-dsl-in-java-mit-jooq","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2011\/07\/gastbeitrag-sql-als-interne-dsl-in-java-mit-jooq\/","title":{"rendered":"Gastbeitrag: SQL als &quot;interne DSL&quot; in Java mit jOOQ"},"content":{"rendered":"<p>Vor einiger Zeit hat Christian auf seinem Blog das fluent API seiner jRTF-Library vorgestellt, welche dem Benutzer erm\u00f6glicht, RTF-Dokumente mit einfachen Sprach-Konstrukten direkt mit Java zu erstellen:<\/p>\n<ul>\n<li>jRTF: <a href=\"http:\/\/www.tutego.de\/blog\/javainsel\/2010\/07\/jrtf-a-new-library-for-building-rtf-documents\/\">http:\/\/www.tutego.de\/blog\/javainsel\/2010\/07\/jrtf-a-new-library-for-building-rtf-documents\/<\/a> <\/li>\n<li>Reflection API: <a href=\"http:\/\/www.tutego.de\/blog\/javainsel\/2009\/02\/fluent-interface-based-api-fur-java-reflection-fest-reflect\/\">http:\/\/www.tutego.de\/blog\/javainsel\/2009\/02\/fluent-interface-based-api-fur-java-reflection-fest-reflect\/<\/a> <\/li>\n<\/ul>\n<p>DSL (Domain Specific Languages) sind ein spannendes Thema in vielen Bereichen. F\u00fcr die meisten Zwecke werden sogenannte &quot;externe&quot; DSL&#8217;s definiert, also f\u00fcr einen technischen oder fachlichen Bereich spezialisierte Sprachen, welche unabh\u00e4ngig von anderen Sprachen interpretiert oder kompiliert werden. Manchmal gelingt es aber auch, solche spezialisierten Sprachen in einer anderen Sprache zu &quot;internalisieren&quot;, wie dies eben mit jRTF gelungen ist. Heute m\u00f6chte ich eine \u00e4hnliche DSL pr\u00e4sentieren, welche ich in jOOQ (Java Object Oriented Querying) eingebaut habe. jOOQ bildet SQL als &quot;interne DSL&quot; in Java ab, so dass Datenbank-Abfragen direkt in Java formuliert werden k\u00f6nnen. Ein Beispiel:<\/p>\n<\/p>\n<pre class=\"prettyprint\">\/\/ Suche alle B\u00fccher, welche in 2011 publiziert wurden\ncreate.select()\n      .from(BOOK)\n      .where(PUBLISHED_IN.equal(2011))\n      .orderBy(TITLE)\n      .fetch();<\/pre>\n<\/p>\n<p>Dabei steht BOOK f\u00fcr eine Tabelle mit den Feldern PUBLISHED_IN und TITLE. Alle diese Objekte werden von jOOQ generiert.<\/p>\n<p><!--more--><\/p>\n<h4>SQL &#8211; Grundlagen und Motivation f\u00fcr jOOQ<\/h4>\n<p>SQL ist eine deklarative &quot;externe DSL&quot;, welche von einem Datenbanksystem in beliebiger Form interpretiert werden kann, um das gew\u00fcnschte Resultat zu produzieren. So kann eine vereinfachte SELECT-Syntax beispielsweise wie folgt definiert werden:<\/p>\n<\/p>\n<pre class=\"prettyprint\">SELECT [ ALL | DISTINCT [ ON ( ausdruck [, ...] ) ] ] * | \n        ausdruck [ AS ausgabename ] [, ...]\n    [ FROM from_element [, ...] ]\n    [ WHERE bedingung ]\n    [ GROUP BY ausdruck [, ...] ]\n    [ HAVING bedingung [, ...] ]\n    [ { UNION | INTERSECT | EXCEPT } [ ALL ] select ]\n    [ ORDER BY ausdruck [ ASC | DESC | USING operator ] [, ...] ]\n    [ LIMIT { anzahl | ALL } ]\n    [ OFFSET start ]\n    [ FOR UPDATE [ OF tabellenname [, ...] ] ]<\/pre>\n<\/p>\n<p>Aus dem Postgres-Handbuch:<br \/>\n  <br \/><a href=\"http:\/\/www.postgresql.org\/files\/documentation\/books\/pghandbuch\/html\/sql-select.html\">http:\/\/www.postgresql.org\/files\/documentation\/books\/pghandbuch\/html\/sql-select.html<\/a><\/p>\n<p>Andere Datenbanken, wie z.B. Oracle, haben noch eine viel komplexere Syntax. Es werden zum Beispiel auch Elemente wie <code>[WITH ..], [CONNECT BY ..], [PIVOT ..], [UNPIVOT ..]<\/code> und so weiter unterst\u00fctzt. Auf Details wird hier verzichtet.<\/p>\n<p>F\u00fcr viele Java-Entwickler ist SQL etwas Externes und wird oft missverstanden, und gemieden. SQL funktioniert grundlegend anders als die meisten prozeduralen oder objekt-orientierten Programmiersprachen. Die Vielfalt von SQL-Dialekten und deren enorm n\u00fctzlicher Funktionalit\u00e4t wird unter anderem genau deswegen von den f\u00fchrenden Datenbank-Abstraktions-Frameworks hinter massiven Vereinfachungen versteckt. Zum Beispiel hat Hibernate HQL und criteria queries entwickelt, welche nur sehr rudiment\u00e4re Abfragen erm\u00f6glichen.<\/p>\n<p>jOOQ geht einen anderen Weg und m\u00f6chte dem SQL-orientierten Entwickler erlauben, s\u00e4mtliche Syntax-Konstrukte direkt anzuwenden. Dabei macht sich jOOQ die wichtigsten Eigenschaften einer internen DSL zu nutze.<\/p>\n<ul>\n<li>Die SQL-Abfragen k\u00f6nnen vom Java Compiler formell auf Syntax und Typsicherheit \u00fcberpr\u00fcft werden. <\/li>\n<li>IDE&#8217;s wie Eclipse k\u00f6nnen mit Auto-Vervollst\u00e4ndigung helfen, korrektes SQL zu schreiben <\/li>\n<li>Unterst\u00fctzung der gesamten Standard-SQL Syntax, sowie einiger propriet\u00e4rer Konstrukte. Es gibt keine k\u00fcnstliche Grenze, wie z.B. mit HQL. <\/li>\n<\/ul>\n<h4>Internalisierung einer externen DSL<\/h4>\n<p>In diesem Artikel m\u00f6chte ich aufzeigen, wie eine formell definierte, externe DSL in Java mittels einfacher Regeln &quot;internalisiert&quot; werden kann, und wie dies f\u00fcr SQL in jOOQ gel\u00f6st worden ist. Aber zuerst zu den Grunds\u00e4tzen:<\/p>\n<h5>1. Versteckte Implementierung<\/h5>\n<p>Der wichtigste Schritt ist der allererste. Eine &quot;interne&quot; DSL ist schwer vorstellbar, ohne das Abstraktionsverm\u00f6gen von Java&#8217;s Schnittstellen zu verwenden. Um eine Implementierung der verschiedenen DSL-Schnittstellen zu konstruieren ben\u00f6tigen wir eine Factory. Je nach Funktionalit\u00e4tsbedarf kann die Factory statische oder nicht-statische Methoden anbieten. Der Vorteil von statischen Methoden ist die M\u00f6glichkeit der Verwendung von static imports, welche den Code noch lesbarer machen. Der Vorteil von nicht-statischen Methoden ist die M\u00f6glichkeit, dass die konstruierten Objekte eine Referenz auf die Factory halten k\u00f6nnen.<\/p>\n<h5>2. Schl\u00fcsselw\u00f6rter<\/h5>\n<p>Idealerweise wird jedes Schl\u00fcsselwort der externen DSL durch eine Java-Methode &quot;internalisiert&quot;. Zusammengesetzte Schl\u00fcsselw\u00f6rter (wie z.B. GROUP BY) werden dabei wie in Java \u00fcblich durch &quot;Camel Case&quot; zusammengesetzt (also z.B. groupBy). Somit werden die SQL-Schl\u00fcsselw\u00f6rter<\/p>\n<\/p>\n<pre class=\"prettyprint\">SELECT .. FROM .. JOIN .. ON .. WHERE .. GROUP BY<\/pre>\n<\/p>\n<p>durch die entsprechenden Methoden modelliert:<\/p>\n<\/p>\n<pre class=\"prettyprint\">.select(...).from(...).join(...).on(...).where(...).groupBy(...)<\/pre>\n<\/p>\n<h5>3. Sonderzeichen<\/h5>\n<p>Die externe DSL spezifiziert oft Sonderzeichen, wie <code>( ) || + -<\/code> und so weiter. Diese Sonderzeichen k\u00f6nnen in Java nicht abgebildet werden. Andere Sprachen, wie z.B. C++, C# haben m\u00e4chtigere Mittel um auch Operatoren zu \u00fcberladen. In Java werden sie am besten einfach ignoriert, oder in extremeren Sonderf\u00e4llen ausformuliert &#8211; also .parentheses() .closingParentheses() .concat() .plus() .minus()<\/p>\n<h5>4. Klauseln<\/h5>\n<p>Eine Klausel wird meist in einer Form \u00e4hnlich dieser Formen hier formuliert:<\/p>\n<\/p>\n<pre class=\"prettyprint\">a) EIN EINZELNES WORT\nb) EIN PARAMETRISIERTES WORT parameter\nc) EIN WORT [ EIN OPTIONALES WORT ]\nd) EIN WORT { EIN WAHLWORT | EIN ANDERES WAHLWORT }\ne) EIN WORT [ , EIN WORT ... ]<\/pre>\n<\/p>\n<p>Die Internalisierung dieser Definitionen erfolgt so:<\/p>\n<h6>a) Die Grundklausel<\/h6>\n<pre class=\"prettyprint\">class Factory {\n    \/\/ Nach EIN EINZELNES WORT geht's nicht mehr weiter.\n    \/\/ Es k\u00f6nnen keine weiteren Klauseln angeh\u00e4ngt werden\n    Schluss einEinzelnesWort();\n}\n\ninterface Schluss {\n    \/\/ Hier k\u00f6nnen grunds\u00e4tzliche Methoden der DSL deklariert werden\n    \/\/ z.B. execute(), run(), etc\n    void execute();\n}\n\n\/\/ Anwendung\nnew Factory().einEinzelnesWort().execute();<\/pre>\n<h6>b) Eine Klausel mit Parameter, z.B. einem numerischen Parameter<\/h6>\n<pre class=\"prettyprint\">class Factory {\n    \/\/ Bei &quot;kompatibler&quot; Syntax kann die &quot;Schluss-Schnittstelle&quot;\n    \/\/ wiederverwendet werden\n    Schluss einParametrisiertesWort(int parameter);\n}\n\n\/\/ Anwendung\nnew Factory().einParametrisiertesWort(42).execute();<\/pre>\n<p><\/code><\/p>\n<h6>c) Optionale Klauseln<\/h6>\n<pre class=\"prettyprint\">class Factory {\n    \/\/ Nach EIN WORT k\u00f6nnen noch weitere Klauseln hinzugef\u00fcgt werden. \n    \/\/ Deswegen wird OptionalerSchritt zur\u00fcckgegeben\n    OptionalerSchritt einWort();\n}\n\n\/\/ Dadurch, dass OptionalerSchritt von Schluss erbt, k\u00f6nnen die hier\n\/\/ deklarierten Methoden (=Schl\u00fcsselw\u00f6rter) \u00fcbersprungen werden\ninterface OptionalerSchritt extends Schluss {\n    \/\/ Danach ist aber trotzdem Schluss\n    Schluss einOptionalesWort();\n}\n\n\/\/ Anwendungen\nnew Factory().einWort().execute();\nnew Factory().einWort().einOptionalesWort().execute();<\/pre>\n<h6>d) Wahl-Klauseln<\/h6>\n<pre class=\"prettyprint\">class Factory {\n    \/\/ Wie zuvor k\u00f6nnen nach EIN WORT weitere Klauseln\n    \/\/ hinzugef\u00fcgt werden\n    WahlSchritt einWort();\n}\n\n\/\/ Eine Wahl ist nicht optional, es muss eine weitere Klausel\n\/\/ folgen. Entsprechend erbt die Schnittstelle NICHT\ninterface WahlSchritt {\n    \/\/ Es kann gew\u00e4hlt werden zwischen...\n    Schluss einWahlwort();\n    \n    \/\/ ... und ...\n    Schluss einAnderesWahlwort();\n    \n    \/\/ In jedem Fall gelangt man zum Schluss\n}\n\n\/\/ Anwendungen\nnew Factory().einWort().einWahlwort().execute();\nnew Factory().einWort().einAnderesWahlwort().execute();<\/pre>\n<h6>e) Wiederholungen<\/h6>\n<pre class=\"prettyprint\">class Factory {\n    \/\/ Gewisse Klauseln k\u00f6nnen beliebig oft aneinandergereiht werden.\n    WiederholungsSchritt einWort();\n}\n\n\/\/ Weitere Wiederholungen sind optional. Dies wird auch hier mit Vererbung\n\/\/ gel\u00f6st\ninterface WiederholungsSchritt extends Schluss {\n    \/\/ Bei Wiederholung gelangt man zur\u00fcck zum Ausgangstyp\n    WiederholungsSchritt einWort();\n}\n\n\/\/ Anwendungen\nnew Factory().einWort().execute();\nnew Factory().einWort().einWort().execute();\nnew Factory().einWort().einWort().einWort().execute();<\/pre>\n<p>Die genannten Regeln lassen sich beliebig kombinieren und verschachteln, so dass fast alle m\u00f6glichen Syntaxb\u00e4ume abbildbar sind. Dadurch kann eine relativ grosse Anzahl Schnittstellen entstehen, was f\u00fcr heutige Compiler und ClassLoader aber kein Problem mehr darstellen sollte.<\/p>\n<h4>SQL als interne DSL in jOOQ<\/h4>\n<p>Das f\u00fcr jOOQ verwendete fluent API baut auf einer Hierarchie von Schnittstellen auf, welche etwas vereinfacht so definiert ist (die tats\u00e4chliche Umsetzung weicht etwas von diesem Beispiel ab):<\/p>\n<\/p>\n<pre class=\"prettyprint\">\/\/ Die Factory-Klasse ist der Einstiegspunkt f\u00fcr die DSL.\n\/\/ Alle Objekte werden von hier konstruiert\nclass Factory {\n\n    \/\/ Es k\u00f6nnen 1..n Felder selektiert werden\n    SelectFromStep select(Field... fields);\n}\n\n\/\/ Dieser &quot;Schritt&quot; oder diese Klausel der SELECT Syntax\n\/\/ ist obligatorisch. Man muss also .from() aufrufen\ninterface SelectFromStep {\n\n    \/\/ Es kann von 1..n Tabellen selektiert werden\n    SelectJoinStep from(Table... tables);\n}\n\n\/\/ Dieser &quot;Schritt&quot; der SELECT Syntax ist optional. Er erbt also vom n\u00e4chsten\n\/\/ Schritt, falls JOIN's ausgelassen w\u00fcrden\ninterface SelectJoinStep extends SelectWhereStep {\n\n    \/\/ Eine Tabelle kann hinzugef\u00fcgt werden\n    SelectOnStep join(Table table);\n}\n\n\/\/ Nach einer JOIN-Klausel muss zwingend eine ON- oder USING-Klausel folgen.\n\/\/ Danach ist eine weitere JOIN-Klausel m\u00f6glich.\ninterface SelectOnStep {\n    SelectJoinStep on(Condition condition);\n    SelectJoinStep using(Field... fields);\n}\n\n\/\/ Auch die WHERE-Klausel ist optional.\ninterface SelectWhereStep extends SelectOrderByStep {\n\n    \/\/ Der Einfachheit halber werden GROUP BY, HAVING Klauseln \u00fcbersprungen.\n    SelectOrderByStep where(Condition condition);\n}\n\n\/\/ Auch die ORDER BY-Klausel ist optional.\ninterface SelectOrderByStep extends SelectFinalStep {\n    \n    \/\/ Der Einfachheit halber wird hier abgebrochen\n    SelectFinalStep orderBy(SortField... fields);\n}<\/pre>\n<\/p>\n<p>Und so weiter. Die Implementierung all dieser Schnittstellen \u00fcbernimmt eine einzige, nur der Factory bekannte Klasse, welche s\u00e4mtliche Methoden-Aufrufe registriert und am Schluss eine passende SQL-Abfrage als String ausf\u00fchrt. Mehr Details hierf\u00fcr gibt&#8217;s hier:<br \/>\n  <br \/><a href=\"https:\/\/sourceforge.net\/apps\/trac\/jooq\/wiki\/Manual\/DSL\/SELECT\">https:\/\/sourceforge.net\/apps\/trac\/jooq\/wiki\/Manual\/DSL\/SELECT<\/a><\/p>\n<p>Interessant ist auch die Frage, wie zusammengesetzte Elemente wie Condition und SortField zustande kommen. Diese werden durch von der SELECT-Syntax unabh\u00e4ngige DSL&#8217;s erstellt. So bietet zum Beispiel der Typ Field folgende DSL-Methoden an:<\/p>\n<\/p>\n<pre class=\"prettyprint\">interface Field {\n    \n    \/\/ Erstellen von Conditions\n    Condition equal(Field field);\n    Condition equal(Object value);\n    Condition notEqual(Field field);\n    Condition notEqual(Object value);\n    \/\/ [...] und noch viele mehr\n    \n    \/\/ Erstellen von spezialisierten Feldern f\u00fcr die ORDER BY Klausel\n    SortField asc();\n    SortField desc();\n}<\/pre>\n<\/p>\n<p>Diese Typen wiederum erlauben weitere DSL-Schritte:<\/p>\n<\/p>\n<pre class=\"prettyprint\">interface Condition {\n\n    \/\/ Zusammensetzen von Conditions\n    Condition and(Condition condition);\n    Condition andNot(Condition condition);\n    Condition or(Condition condition);\n    Condition orNot(Condition condition);\n    \n    \/\/ Negieren von Conditions\n    Condition not();\n}\n\ninterface SortField {\n\n    \/\/ Hinzuf\u00fcgen von weiteren Schl\u00fcsselw\u00f6rtern\n    SortField nullsFirst();\n    SortField nullsLast();\n}<\/pre>\n<\/p>\n<h4>Grenzen von internen DSL&#8217;s<\/h4>\n<p>Mit jOOQ ist es bereits m\u00f6glich, sehr komplexe SQL-Abfragen in Java zu formulieren. Dies beinhaltet unter anderem<\/p>\n<ul>\n<li>Verschachtelte Abfragen <\/li>\n<li>Stored Procedures und Functions <\/li>\n<li>Komplexe JOINs <\/li>\n<li>Rekursives SQL (CONNECT BY, bald auch Common Table Expressions) <\/li>\n<li>Window\/Ranking Functions <\/li>\n<li>Bald auch Oracle&#8217;s PIVOT\/UNPIVOT Syntax <\/li>\n<\/ul>\n<p>Trotzdem kann eine interne DSL nie die M\u00e4chtigkeit ihrer abgebildeten externen DSL erreichen. Die gr\u00f6ssten Limitierungen in jOOQ sind zum Beispiel<\/p>\n<h5>1. Aliasing<\/h5>\n<p>In SQL k\u00f6nnen Tabellen und Felder einfach mit Namen versehen werden, zum Beispiel um Kinder-Elemente mit ihren Eltern-Elementen zu verkn\u00fcpfen:<\/p>\n<\/p>\n<pre class=\"prettyprint\">SELECT *\nFROM my_table a, my_table b\nWHERE <a href=\"http:\/\/a.id\/\">a.id<\/a> = b.parent<\/pre>\n<\/p>\n<p>Dies ist in jOOQ etwas umst\u00e4ndlicher:<\/p>\n<\/p>\n<pre class=\"prettyprint\">Table&lt;?&gt; a = MY_TABLE.as(&quot;a&quot;);\nTable&lt;?&gt; b = MY_TABLE.as(&quot;b&quot;);\n\ncreate.select()\n      .from(a, b)\n      .where(a.getField(ID).equal(b.getField(PARENT)));<\/pre>\n<\/p>\n<h5>2. Grenzen der Syntax<\/h5>\n<p>Wie bereits erw\u00e4hnt k\u00f6nnen SQL-spezifische Sonderzeichen nicht ber\u00fccksichtigt werden, auch wenn diese sinnvoll f\u00fcr die Lesbarkeit w\u00e4ren. Umgekehrt f\u00fcgt jeder Methodenaufruf() ein weiteres Klammern-Paar hinzu. Dies kann schon Lisp-\u00e4hnliche Zust\u00e4nde erreichen \ud83d\ude42<\/p>\n<p>Man beachte die vier Klammern am Schluss:<\/p>\n<\/p>\n<pre class=\"prettyprint\">create.select()\n      .from(BOOK)\n      .where(<a href=\"http:\/\/book.author_id.in\/\">Book.AUTHOR_ID.in<\/a>(create.select(Author.ID)\n                                     .from(AUTHOR)\n                                     .where(Author.BORN.equal(1920))));<\/pre>\n<\/p>\n<h5>3. Nur die Syntax kann validiert werden, nicht die Semantik<\/h5>\n<p>In SQL ist es zum Beispiel nicht m\u00f6glich, bei einer gruppierten Abfrage ein Feld zu selektieren, welches nicht auch in der GROUP BY Klausel Auftritt. Dies ist zum Beispiel nicht korrekt:<\/p>\n<\/p>\n<pre class=\"prettyprint\">SELECT id\nFROM book\nGROUP BY title<\/pre>\n<\/p>\n<p>jOOQ kann dies nicht zur Kompilierzeit \u00fcberpr\u00fcfen, und somit ist folgende Abfrage (in Java) m\u00f6glich:<\/p>\n<\/p>\n<pre class=\"prettyprint\">create.select(ID)\n      .from(BOOK)\n      .groupBy(TITLE)<\/pre>\n<\/p>\n<h4>Fazit<\/h4>\n<p>jOOQ ist ein sehr aktives Projekt und wie jRTF ein gutes Beispiel f\u00fcr den Nutzen eines fluent API in der modernen Java-Welt. Mit jOOQ k\u00f6nnen SQL-bewandte Programmierer wie auch SQL-Anf\u00e4nger von der Typsicherheit einer internen DSL, sowie vom Funktionalit\u00e4tsumfang ihrer Datenbank vollumf\u00e4nglich profitieren, und dies ohne relevanten Performance-Overhead.<\/p>\n<p>Weitere Details auf <a href=\"http:\/\/www.jooq.org\/\">http:\/\/www.jooq.org<\/a><\/p>\n<p>Und auf der User-Group: <a href=\"http:\/\/groups.google.com\/group\/jooq-user\">http:\/\/groups.google.com\/group\/jooq-user<\/a><\/p>\n<p>Vielen Dank auch an Christian, daf\u00fcr dass er mir die M\u00f6glichkeit eines Gast-Artikels gegeben hat.<\/p>\n<p>Lukas Eder (<a href=\"mailto:lukas.eder@gmail.com\">lukas.eder@gmail.com<\/a>)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Vor einiger Zeit hat Christian auf seinem Blog das fluent API seiner jRTF-Library vorgestellt, welche dem Benutzer erm\u00f6glicht, RTF-Dokumente mit einfachen Sprach-Konstrukten direkt mit Java zu erstellen: jRTF: http:\/\/www.tutego.de\/blog\/javainsel\/2010\/07\/jrtf-a-new-library-for-building-rtf-documents\/ Reflection API: http:\/\/www.tutego.de\/blog\/javainsel\/2009\/02\/fluent-interface-based-api-fur-java-reflection-fest-reflect\/ DSL (Domain Specific Languages) sind ein spannendes Thema in vielen Bereichen. F\u00fcr die meisten Zwecke werden sogenannte &quot;externe&quot; DSL&#8217;s definiert, also f\u00fcr einen [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","_links_to":"","_links_to_target":""},"categories":[1],"tags":[],"class_list":["post-1033","post","type-post","status-publish","format-standard","hentry","category-allgemein"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/1033","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/comments?post=1033"}],"version-history":[{"count":5,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/1033\/revisions"}],"predecessor-version":[{"id":1038,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/1033\/revisions\/1038"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=1033"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=1033"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=1033"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}