I allow myself to move this discussion to the jOOQ user group since I
deem the general topic and ideas behind our two frameworks to be of
interest to a broader public. Also, maybe this will capture Timo
Westkämper's attention (also a German-speaker), who is behind another
very nice framework with similar goals: QueryDSL. In general, the user
group is in English, but since we started in German on Christian
Ullenboom's blog, I guess non-German-speakers can follow this
particular thread using Google Translate. Here's the original blog
entry:
http://www.tutego.de/blog/javainsel/2011/07/gastbeitrag-sql-als-interne-dsl-in-java-mit-jooq/
Start of the German thread:
----
> Viel wichtiger ist eh die Richtung:
> Java Klassen -> DB Tabellen.
Das ist durchaus ein gangbarer Ansatz, ähnlich wie Hibernate/JPA.
Wichtig ist, dass ein framework eine klare Vision verfolgt. Diejenige
von SqlEngine ist offensichtlich die Idee, dass das Schema in Java
geschrieben wird. Davon distanziert sich jOOQ allerdings klar durch
die Aussage:
How does jOOQ help you?
> Your database always comes FIRST! That's where
> the real schema is, not in your Java code or some
> XML mapping file.
Somit richtet sich jOOQ historisch primär an Entwickler, welche auf
Teile von komplexen, verteilten Systemen in mittelgrossen bis grossen
Datenbanken zugreifen. Damit meine ich zwischen 1000 und 10000
Tabellen und anderen Schema-Objekten. Solche Systeme sind sehr
langlebig und überdauern in der Regel Java/C++/C# und viele andere
Clients, welche darum herum geschrieben werden. Ein interessanter
Artikel dazu (auch auf der jOOQ-Seite):
http://database-programmer.blogspot.com/2010/12/historical-perspective-of-orm-and.html
Insbesondere der Absatz über "Persistence" gefällt mir. Dies ist
natürlich eine Sicht der Dinge, welche nicht "besser" oder
"schlechter" als eine andere ist. Aber sie ist angepasst an den
Bedürfnissen von Entwicklern und Architekten, die an einem System
arbeiten, in dem es höchst relevant ist, ob der Oracle-spezifische
functional-based Bitmap-Index auf Spalte X im selben Tablespace liegt
wie die Tabelle selbst. Diese Art von Kontrolle über ein Datenschema
ist mit dem Ansatz von SqlEngine kaum erreichbar oder wünschenswert,
aber für die Benutzer von SqlEngine wahrscheinlich auch nicht von
Bedeutung.
> Also ich meine keine ORM Projektion, sondern aus klassenmäßigen SQL Framework Representationen dann in der DB Tabellen erzeugen. Damit hat man alles aus einem Guss, kann von der Logikseite aus Dinge wie Initialdaten usw reinbringen. Sogar OOP Vererbung für die Definition der Tabellen verwenden (wenn auch nicht für die Tabellen selbst in der DB).
Interessant wäre ein Mapping von Vererbung direkt auf die
darunterliegende Datenbank, dort wo das unterstützt wird. Zum Beispiel
in Postgres:
http://www.postgresql.org/docs/9.0/static/ddl-inherit.html
Ich werde in nächster Zeit (logischerweise) den umgekehrten Weg
überprüfen. Das generieren von Java-Klassenhierarchien aus
Postgres-Tabellenvererbung. Dies ist allerdings keine Priorität.
> Beispielsweise besteht das Deployment meines Projekts in der Arbeit einfach nur aus dem Starten der Anwendung. Der Rest (Tabellen, Indizes, etc. anlegen, Initialdaten holen, usw.) geschieht automatisch auf generischem Weg.
Das kann für einen Java-Entwickler in der Tat sehr praktisch sein,
auch für Projekte, welche auf der "grünen Wiese" beginnen. Ideal ist
so etwas z.B. für ein Forum, Blog, etc. Einfach .war-File deployen,
JDBC-Datenquelle konfigurieren und los geht's! Es würde mich
insbesondere interessieren, inwiefern QueryDSL diesen Ansatz
unterstützt. QueryDSL bietet ebenfalls DDL Befehle an. Siehe ganz
unten auf dieser Seite:
http://blog.mysema.com/2011/01/querying-in-sql-with-querydsl.html
> Dialekt Abstraktion war das was ich knapp mit "Wegabstrahieren" bezeichnet hatte. Geht bei DDL dann natürlich auch um Datentypen, etc.
> Das war eine der Hauptmotivationen: komplette DB-unabhängigkeit (die JDBC allein ja längst nicht liefert).
> Einfach alle SQLs abstrahiert entwickeln und wenns sein muss (hatten wir tatsächlich mal im Projekt) kann der Datenbankhersteller mit minimalem Aufwand einfach ausgetauscht werden.
Aus Java-Sicht ist das natürlich lobenswert. Mit Hibernate/JPA oder
jOOQ-Konkurrenzprodukten wie QueryDSL, welches sich sehr schön in JPA
integriert und Criteria Query ersetzt, kann dieselbe Strategie
gefahren werden. Mit jOOQ stelle ich mich auf den Standpunkt, dass bei
grossen Systemen die Datenbank ein sehr essentieller Bestandteil
darstellt, und nicht einfach als "Persistenz-Topf" verwendet wird.
Unter Umständen sind auch mehrere Datenbanken von verschiedenen
Herstellern am System beteiligt. Es werden hunderte von Stored
Procedures geschrieben, welche die Vorzüge der Datenbank des einen
Herstellers ausnutzen. Dies gilt im Besonderen für DB2, Oracle oder
SQL Server. Siehe dazu auch mein Artikel über Stored Procedures:
http://java.dzone.com/articles/2011-great-year-stored
> Das Substutieren von Funktionalität ist interessant. Das mach ich bisher noch nicht. Teils absichtlich, teil weil ich es in den projekten nicht brauche.
Anders lässt sich die Aussage "mit minimalem Aufwand austauschen" kaum
erhärten...?
> Egal ob SqlEngine oder jOOQ, ich finde solche eine API Lösungen noch eleganter als so "built-in" Artefakte wie C#'s LINQ, weil besser in den Programmablauf integrierbarer usw. Insofern freu ich mich fast, dass Java kein LINQ Pendant hat *gg*.
Hast du denn schon mit LINQ gearbeitet? Bei LINQ geht es ja nicht nur
um LINQ to SQL, sondern ganz generell darum, dass die Sprache und die
Syntax von C# mit formell auf Compiler-Stufe um neue Elemente
erweitert werden kann, in diesem Fall für generelle Abfrage-DSL's.
Somit kann also formell überprüft werden, ob der Tabellenalias, von
dem man ein Feld dereferenziert, auch korrekt deklariert wurde. So
etwas ist weder mit jOOQ noch mit SqlEngine möglich...
Freu mich auf weiteres Feedback
Gruss,
Lukas
Am 15. Juli 2011 18:06 schrieb "Thomas Münz" <Thoma...@gmx.de>:
> Hallo Lukas
>
> (ich ergreif einfach mal das Du, Internet- und Informatiker-mentalitätsmäßig)
>
>
> Ich weiß nicht, wann der Ullenboom den Kommentar freischaltet (wieso muss auf einmal freigeschalten werden? Evtl zu lang, hehe), darum schick ich ihn gleich nochmal per Mail.
>
> Ich kann ja zu ORM und bei Bedarf zu Collections auch noch was schreiben =)
> ---------------------
>
> Hi
>
> Danke für die Antwort. Denke aber ab einem bestimmten Punkt muss das wohl eher in Mails verlagert werden :).
>
> FETCH FIRST n ROW[S] ONLY ist tatsächlich inzwischen schon im SQL Standard, oder einem Randteil davon, aber sei's drum. Das ist natürlich nur ein Detail.
>
> Code Generator:
> Klar. Von DB Tabellen nach Java Klassen, hab ich gelesen.
> Ich will jetzt nicht das leidige "hab ich auch alles" Spielchen anfangen. Viel wichtiger ist eh die Richtung:
> Java Klassen -> DB Tabellen.
> Also ich meine keine ORM Projektion, sondern aus klassenmäßigen SQL Framework Representationen dann in der DB Tabellen erzeugen.
> Der große Unterschied ist nämlich:
> Damit hat man alles aus einem Guss, kann von der Logikseite aus Dinge wie Initialdaten usw reinbringen. Sogar OOP Vererbung für die Definition der Tabellen verwenden (wenn auch nicht für die Tabellen selbst in der DB).
> Beispielsweise besteht das Deployment meines Projekts in der Arbeit einfach nur aus dem Starten der Anwendung. Der Rest (Tabellen, Indizes, etc. anlegen, Initialdaten holen, usw.) geschieht automatisch auf generischem Weg.
> Auch wichtig um wie gesagt nicht ständig an zwei Seiten rumwarten zu müssen (Javaklassen aus Tabellen neu generieren lassen erzeugt halt massig Compiler Fehler und damit Nacharbeitungsaufwand. Tabellenklassen live refatoren und dann automatisiert die DB Struktur anpassen lassen hat dieses problem nicht).
> Das entzerrt dann natürlich wieder ganz enorm Programmarchitekturen, weil die DB "nur" noch ein "dummer" von Java aus gesteuerter Container ist und mit der Business Logik nichts mehr zu tun hat.
>
> Dialekt Abstraktion war das was ich knapp mit "Wegabstrahieren" bezeichnet hatte. Geht bei DDL dann natürlich auch um Datentypen, etc.
> Das war eine der Hauptmotivationen: komplette DB-unabhängigkeit (die JDBC allein ja längst nicht liefert).
> Einfach alle SQLs abstrahiert entwickeln und wenns sein muss (hatten wir tatsächlich mal im Projekt) kann der Datenbankhersteller mit minimalem Aufwand einfach ausgetauscht werden.
>
> Das Substutieren von Funktionalität ist interessant. Das mach ich bisher noch nicht. Teils absichtlich, teil weil ich es in den projekten nicht brauche.
>
> ORM: Okay dann hab ich das missverstanden. Gut dass mal einer saubere Grenzen zwischen SQL Abfragedefinition und ORM zieht :-)
>
>
> Jedenfalls muss ich sagen, dass es natürlich eine zweifelhafte Legitimität von mir hat, ohne Webseite, Doku, etc. trotzdem rumzukritisieren. Bzw wenn ich das schon hätte würde ich schon längst mit dem Framework hausieren gehen :D.
> Von diesem Punkt her ist jOOQ natürlich klar "überlegen". Einfach weil es dokumentiert verwendbar ist und mein Code eigentlich nur ein Open Source firmeninternes Werkzeug ist.
>
> Darum ja der Ärger über mich selbst :D.
>
> Quellcode gibt's hier: http://jadoth.sourceforge.net/
> Allerdings hab ich 2010 ein verbessertes Collections framework "dazwischengeschoben", an dem ich immer noch hänge (siehe blog http://www.jadoth.net), darum längere Zwangspause.
> War wohl auch die Pause, die es ermöglicht hat, dass vergleichbare Frameworks aufkommen bevor ich meins soweit releasereif habe. Denn seit Beginn meiner Arbeiten hab ich immer wieder nach vergleichbaren Frameworks gesucht, aber nie was ernstnehmbares gefunden (bis heute ^^).
>
> Naja Rest per Mail, denk ich.
>
>
> Ach doch, noch was:
> Egal ob SqlEngine oder jOOQ, ich finde solche eine API Lösungen noch eleganter als so "built-in" Artefakte wie C#'s LINQ, weil besser in den Programmablauf integrierbarer usw. Insofern freu ich mich fast, dass Java kein LINQ Pendant hat *gg*.
>
I could surely write all the stuff in english to fit to the google group (hi there), but honestly I'm currently too lazy for it :(.
So I go on writing in German.
------------
Vielen Dank, das waren alles sehr interessante Absätze und Links.
Es ist natürlich sehr legitim, pragmatisch und auch benötigt, einen Ansatz von "Your database comes first" zu bieten.
Sicher gibt es mehr bestehende Systeme als "grüne Wiese" Situationen. Darum werden in der SqlEngine auch beide Generierungsrichtungen "DB -> Java" und "Java -> DB" verfolgt.
Meine Erfahrung ist jedoch (leider, muss ich sagen), dass vor lauter Altsystemargumentation die Grüne Wiese, bei der echte Innovationen vorangetrieben werden könnten, vernachlässigt wird. Wenn man dann mal eine Grüne Wiese vor sich hat verfolgt man doch wieder die alten Ansätze, von denen man eigentlich weiß, dass sie suboptimal sind weil a) man es so schon immer gemacht hat und b) die Technologien die innovativen Ansätze ausgeklammert haben, fast wie eine Art self-fulfilling Prophecy. Und dadurch ändert sich "nie" oder nur sehr langsam etwas.
Darum sehe ich den Grüne-Wiese-Ansatz entgegem allem Pragmatismus als den wichtigeren an und die Altsystemkompatibilität eher als das Stiefkind.
Unter dieser Philosophie steht auch das ORM Konzept:
Man kann natürlich versuchen, wie JPA das macht, zwei im Detail unvereinbare Welten - ER und OOP - doch irgendwie so halb und halb zusammenzumixen. Das Ergebnis davon ist natürlich unweigerlich auf beiden Seiten unbefriedigend: OOP-seitig macht man alles "unnötig" kompliziert, eingeschränkt, unsauber und auf der ER Seite ist trotzdem nicht das alles möglich, was ein ER-Design eigentlich machen würde.
Für mich ist soetwas bestenfalls eine pragmatische Workaround Lösung, aber keine saubere Technologie.
Wenn man ORM machen will, dann liegt der Designschwerpunkt sowieso auf der OOP Seite. Und die wird kompromissmlos in den "Persistenztopf" projiziert. Das ist erstaunlich einfach, wenn man 2-3 neue Konzepte verwendet - und auf einmal verschwindet eine unendliche Menge an Problemen, die man sich mit dem "aber wir müssen Altsystem-kompatibel bleiben"-Ansatz einhandelt (und deren erhöhter Behandlungsaufwand wahrscheinlich einer sauberen Portierung auf ein neues System gleich kommt).
Man kann darauf immer noch mit SQL zugreifen und auch gut strukturiert Änderungen vornehmen. Aber die komplexen Änderungen finden ja doch auf Seite der Businesslogik, also im Anwendungscode statt.
Der Artikel "Historical Perspective of ORM and Alternatives" ist schon interessant und enthält natürlich viel richtiges, aber mir ist es etwas zu einfach. Da werden die guten alten Zeiten von "früher ging es doch auch" beschworen, als alles noch "ganz einfach" war.
Mir kommt es vor als würden da Komplexität-10 Äpfel von früher mit Komplexität-1000000 Birnen von heute verglichen.
Es geht bei OOP ja nicht darum, "schönen" Code um seiner selbst Willen zu haben, sondern um Komplexität besser zu handeln.
Bei "The tables [...] have a place for everything and everything in its place, and after that the application code mostly writes itself." hab ich dann aufgehört zu lesen. Zu realitätsfremd. Und das sage ich wohlgemerkt als jemand, dessen aktuelles Projekt zu 90% aus Datenmengenoperationen in der Datenbank besteht. Die Tabellen dafür zu definieren ist der leichteste Teil. Der interessante Teil ist, effizient strukturierten Code zu schreiben der die ganze Komplexität abbildet und trotzdem übersichtlich, flexibel, leicht wartbar, etc. ist.
Und dafür muss alles "aus einem Guss" sein (und es klappt tatsächlich).
Die rhetorische Frage "Anders lässt sich die Aussage "mit minimalem Aufwand austauschen" kaum erhärten...?" hab ich nicht verstanden in dem Zusammenhang :-/. Mir ist aber aufgefallen, dass ich "Substituieren" komplett falsch geschrieben hatte :DD.
Mit LINQ habe ich noch nicht gearbeitet. Zugegeben. In den Zusammenhängen, wo ich darauf gestoßen bin ging es auch eher um Arbeiten auf Collections.
Wie auch immer, mein Punkt war nur: Ich finde Lösungen über APIs (jOOQ, usw.) besser als fremde Sprachen als Artefakte in der Hauptsprache einzubetten, einfach weil sie sich besser in die Programmlogik integrieren lassen.
Oh ich wollte noch was konkret zu dem Tutego Gastbeitrag schreiben:
An folgendem Beispiel werden die vielen nötigen Klammern selbstkritisch angezeigt:
create.select()
.from(BOOK)
.where(Book.AUTHOR_ID.in(create.select(Author.ID)
.from(AUTHOR)
.where(Author.BORN.equal(1920))));
Nun wäre die Zahl der Klammern bei der SqlEngine natürlich genauso ;) (die Syntax ist in diesem Beispiel fast identisch, außer dass ich SQL-Syntax entsprechende Methoden in capslock schreibe, also SELECT usw. Man darf das nicht als "Bruch" mit der Java Naming Convention sehen, sondern als Erweiterung zur deutlichen Anzeige der SQL Konstrukte).
Worauf ich stattdessen hinaus will ist:
Dieses Einrückungsschema ist ein Graus. Wenn man das verbessert lindert sich das Problem mit den Klammern schon deutlich.
Zum einen das "create.", das schon gleich mal eine Einrückung nach sich zieht.
Was ist "create" eigentlich? Eine klein benannte static util class? Eine Factory Instanz?
Im ersten Fall kann man ja immerhin von static imports gebrauch machen.
Z.B. in meiner SqlEngine: SQL.SELECT()
Mit static import der factory Methode SQL.SELECT() steht dann nur noch da:
SELECT()
.FROM(BOOK)
.WHERE(...) //usw...
Ähnlich mit den Sub-SELECTs. Wieso nicht einfach so:
select()
.from(BOOK)
.where(Book.AUTHOR_ID.in(
select(Author.ID)
.from(AUTHOR)
.where(Author.BORN.equal(1920))
));
So sieht man klar die beiden SELECT-Ebenen entsprechend der Einrückung des Java codes und die "));" schließen optisch ganz natürlich das ".where(" ab. Außerdem kann man zwei Klammern optisch noch leicht zuordnen. Und auf einmal wird alles sehr viel kompakter und trotzdem gleichzeitig übersichtlicher.
Der Einrückungsstil macht das Framework natürlich nicht besser oder schlechter. Ich wollte nur sagen dass man es sich auch unnötig schwer machen kann - aber nicht unbedingt sollte ;).
Ich weiß nicht, wie interessant es an dieser Stelle ist, aber hier ein Link zu dem Java RAD-IDE Produkt unserer Firma, in der meine SqlEngine - abgesehen von unseren Kundenprojekten, die natürlich nicht öffentlich sind - als Kern der Datenbankabfragen verwendet wird:
http://www.xdev-software.de/
Allerdings wird durch die RAD Konzepte nach außen hin nochmal ein anderer Ansatz verfolgt (Vereinfachung, Assistenten), so dass die SqlEngine nur ein internes Modul, aber keine Schnittstelle für den (RAD-)Entwickler mehr ist.
So jetzt hab ich den Großteil des Abends damit geschlachtet, aber war trotzdem interessant :-)
Gruß,
Thomas
-------- Original-Nachricht --------
> Datum: Fri, 15 Jul 2011 19:51:37 +0200
> Von: Lukas Eder <lukas...@gmail.com>
> An: "Thomas Münz" <Thoma...@gmx.de>
> CC: jooq...@googlegroups.com
> Betreff: Re: SqlEngine vs jOOQ