{"id":79465,"date":"2020-04-27T07:42:20","date_gmt":"2020-04-27T05:42:20","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh"},"modified":"2020-04-27T07:42:20","modified_gmt":"2020-04-27T05:42:20","slug":"istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh","status":"publish","type":"post","link":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh","title":{"rendered":"Die Wahrheit zuerst, oder warum Systeme auf Basis der Datenbankstruktur entworfen werden m\u00fcssen","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Hallo, Habra!<\/p>\n<p>Wir setzen unsere Erkundung des Themas fort <noindex><a rel=\"nofollow\" href=\"https:\/\/www.piter.com\/collection\/all\/product\/sovremennyy-yazyk-java-lyambda-vyrazheniya-potoki-i-funktsionalnoe-programmirovanie\">Java<\/a><\/noindex> und <noindex><a rel=\"nofollow\" href=\"https:\/\/storage.piter.com\/upload\/contents\/978544611587\/978544611587_X.pdf\">Fr\u00fchling<\/a><\/noindex>, einschlie\u00dflich auf Datenbankebene. Heute empfehlen wir, dar\u00fcber zu lesen, warum bei der Planung gro\u00dfer Anwendungen die Struktur der Datenbank und nicht der Java-Code entscheidend sein sollte, wie das gemacht wird und welche Ausnahmen es von dieser Regel gibt.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex> <br \/>\nIn diesem etwas versp\u00e4teten Artikel erkl\u00e4re ich, warum ich denke, dass in praktisch allen F\u00e4llen das Datenmodell in einer Anwendung \"von der Datenbank aus\" und nicht \"von den M\u00f6glichkeiten Java aus\" (oder einer anderen Client-Sprache, mit der Sie arbeiten) entworfen werden sollte. Wenn Sie sich f\u00fcr den zweiten Ansatz entscheiden, betreten Sie einen langen Weg voller Schmerzen und Leiden, sobald Ihr Projekt zu wachsen beginnt.<\/p>\n<p>Der Artikel ist inspiriert von <noindex><a rel=\"nofollow\" href=\"https:\/\/stackoverflow.com\/questions\/50706556\/kotlin-with-jooq-and-write-table-models-manually-without-code-generation\">eine Frage<\/a><\/noindex>, die auf Stack Overflow gestellt wurde.<\/p>\n<p>Interessante Diskussionen auf reddit in den Bereichen <noindex><a rel=\"nofollow\" href=\"https:\/\/www.reddit.com\/r\/java\/comments\/8p0uj3\/truth_first_or_why_you_should_mostly_implement\/\">\/r\/java<\/a><\/noindex> und <noindex><a rel=\"nofollow\" href=\"https:\/\/www.reddit.com\/r\/programming\/comments\/8p0qle\/truth_first_or_why_you_should_mostly_implement\/\">\/r\/programming<\/a><\/noindex>.<\/p>\n<h4>Codegenerierung<\/h4>\n<p>\nWie \u00fcberrascht war ich, dass es eine so kleine Schicht von Benutzern gibt, die, nachdem sie mit jOOQ vertraut wurden, sich \u00fcber die Tatsache emp\u00f6ren, dass bei der Arbeit mit jOOQ ernsthaft auf die Codegenerierung vertraut wird. Niemand hindert Sie daran, jOOQ so zu verwenden, wie Sie es f\u00fcr richtig halten, und niemand zwingt Sie zur Codegenerierung. Doch standardm\u00e4\u00dfig (so wie im Handbuch beschrieben) erfolgt die Arbeit mit jOOQ wie folgt: Sie beginnen mit dem (geerbten) Datenbankschema, f\u00fchren dessen R\u00fcckprojektierung mit dem jOOQ-Codegenerator durch, um so eine Reihe von Klassen zu erhalten, die Ihre Tabellen repr\u00e4sentieren, und schreiben dann typsichere Anfragen an diese Tabellen:<\/p>\n<pre><code class=\"java\">\tfor (Record2 record : DSL.using(configuration)\n\/\/   ^^^^^^^^^^^^^^^^^^^^^^^ Typinformationen stammen aus \n\/\/   dem generierten Code, auf den die folgende\n\/\/   SELECT-Bedingung verweist\n \n       .select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)\n\/\/           vvvvv ^^^^^^^^^^^^  ^^^^^^^^^^^^^^^ generierte Namen\n       .from(ACTOR)\n       .orderBy(1, 2)) {\n    \/\/ ...\n}<\/code><\/pre>\n<p>\nDer Code wird entweder manuell au\u00dferhalb der Build-Umgebung oder manuell bei jedem Build generiert. Beispielsweise kann eine solche Regenerierung sofort nach <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2014\/06\/25\/flyway-and-jooq-for-unbeatable-sql-development-productivity\/\">der Flyway-Datenbankmigration folgen, die ebenfalls manuell oder automatisch durchgef\u00fchrt werden kann<\/a><\/noindex>.<\/p>\n<h4>Codegenerierung<\/h4>\n<p>\nBei solchen Ans\u00e4tzen zur Code-Generierung \u2013 sowohl manuell als auch automatisiert \u2013 gibt es verschiedene Philosophien, Vorteile und Nachteile, die ich in diesem Artikel nicht im Detail behandeln m\u00f6chte. Aber insgesamt besteht der Sinn des generierten Codes darin, dass er es erm\u00f6glicht, in Java die \u201eWahrheit\u201c, die wir als gegeben betrachten, entweder innerhalb oder au\u00dferhalb unseres Systems zu reproduzieren. In gewisser Hinsicht tun das auch Compiler, die Bytecode, Maschinencode oder eine andere Art von Code auf Basis der Quelltexte generieren \u2013 wir erhalten eine Darstellung unserer \u201eWahrheit\u201c in einer anderen Sprache, unabh\u00e4ngig von den spezifischen Gr\u00fcnden. <\/p>\n<p>Es gibt viele solcher Code-Generatoren. Zum Beispiel, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/technotes\/tools\/unix\/xjc.html\">XJC kann Java-Code basierend auf XSD- oder WSDL-Dateien generieren.<\/a><\/noindex>Das Prinzip ist immer gleich:<\/p>\n<ul>\n<li>Es gibt eine bestimmte Wahrheit (innere oder \u00e4u\u00dfere) \u2013 zum Beispiel, eine Spezifikation, ein Datenmodell, usw.<\/li>\n<li>Wir ben\u00f6tigen eine lokale Darstellung dieser Wahrheit in unserer Programmiersprache.<\/li>\n<\/ul>\n<p>\nEs ist oft sinnvoll, eine solche Darstellung zu generieren \u2013 um Redundanz zu vermeiden.<\/p>\n<h4>Typanbieter und Annotation-Processing<\/h4>\n<p>\nZur Kenntnis: Ein weiterer, modernerer und spezifischerer Ansatz zur Code-Generierung f\u00fcr jOOQ ist die Verwendung von Typanbietern, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/fsharp\/tutorials\/type-providers\/\">so wie sie in F# implementiert sind.<\/a><\/noindex>In diesem Fall wird der Code vom Compiler generiert, und zwar tats\u00e4chlich w\u00e4hrend des Kompilierungsprozesses. In Form von Quelltext existiert dieser Code prinzipiell nicht. In Java gibt es \u00e4hnliche, wenn auch nicht so elegante Werkzeuge \u2013 das sind die Annotation-Processor, zum Beispiel, <noindex><a rel=\"nofollow\" href=\"https:\/\/projectlombok.org\">Lombok.<\/a><\/noindex>.<\/p>\n<p>In gewisser Hinsicht geschieht hier dasselbe wie im ersten Fall, mit Ausnahme von:<\/p>\n<ul>\n<li>Sie sehen den generierten Code nicht (vielleicht ist eine solche Situation f\u00fcr einige nicht so absto\u00dfend?).<\/li>\n<li>Sie m\u00fcssen garantieren, dass die Typen bereitgestellt werden k\u00f6nnen, d.h. die \u201eWahrheit\u201c muss immer verf\u00fcgbar sein. Das ist einfach bei Lombok, das die \u201eWahrheit\u201c annotiert. Schwieriger wird es mit Datenbankmodellen, deren Funktion von einer st\u00e4ndig verf\u00fcgbaren aktiven Verbindung abh\u00e4ngt.<\/li>\n<\/ul>\n<p><\/p>\n<h4>Was ist das Problem mit der Code-Generierung?<\/h4>\n<p>\nNeben der kniffligen Frage, wie man die Code-Generierung besser starten kann - manuell oder automatisch - muss auch erw\u00e4hnt werden, dass es Menschen gibt, die der Meinung sind, dass die Code-Generierung \u00fcberhaupt nicht n\u00f6tig ist. Der h\u00e4ufigste Grund f\u00fcr diese Sichtweise, dem ich begegnet bin, ist, dass es dann schwierig ist, die Build-Pipeline einzurichten. Ja, das ist in der Tat schwierig. Es entstehen zus\u00e4tzliche Infrastrukturkosten. Wenn Sie gerade erst anfangen, mit einem bestimmten Produkt zu arbeiten (sei es jOOQ, JAXB, Hibernate usw.), ben\u00f6tigen Sie Zeit, um die Arbeitsumgebung einzurichten, die Sie lieber mit dem Studium der API verbringen w\u00fcrden, um dann den Wert daraus zu sch\u00f6pfen.<\/p>\n<p>Wenn die Kosten zu hoch sind, um sich mit dem Aufbau des Generators auseinanderzusetzen - dann hat das API tats\u00e4chlich schlecht an der Benutzerfreundlichkeit des Code-Generators gearbeitet (und es zeigt sich sp\u00e4ter, dass auch die Benutzereinstellungen darin kompliziert sind). Die Benutzerfreundlichkeit sollte von gr\u00f6\u00dfter Priorit\u00e4t f\u00fcr jedes solche API sein. Aber das ist lediglich ein Argument gegen die Code-Generierung. Ansonsten ist es absolut notwendig, die lokale Darstellung der inneren oder \u00e4u\u00dferen Wahrheit vollst\u00e4ndig manuell zu schreiben.<\/p>\n<p>Viele werden sagen, dass sie keine Zeit haben, sich darum zu k\u00fcmmern. Sie haben Fristen f\u00fcr ihr Superprodukt. Irgendwann werden wir die Build-Pipelines in Ordnung bringen, das wird schon klappen. Ich antworte ihnen:<\/p>\n<p><img decoding=\"async\" alt=\"Die Wahrheit zuerst, oder warum Systeme auf Basis der Datenbankstruktur entworfen werden m\u00fcssen\" src=\"\/wp-content\/uploads\/2020\/04\/cd9aca1e0365fa5af28cfc72c201cb8c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/www.flickr.com\/photos\/toddle_email_newsletters\/15596940251\/in\/photostream\/\">Original<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"http:\/\/audiencestack.com\/static\/blog.html\">Alan O'Rourke, Audience Stack<\/a><\/noindex> <\/p>\n<p><b>Aber in Hibernate \/ JPA ist es so einfach, Code \u201ef\u00fcr Java\u201c zu schreiben. <\/b><\/p>\n<p>In der Tat. F\u00fcr Hibernate und seine Benutzer ist es gleichzeitig ein Segen und ein Fluch. In Hibernate kann man einfach ein paar Entit\u00e4ten so schreiben:<\/p>\n<pre><code class=\"java\">\t@Entity\nclass Book {\n  @Id\n  int id;\n  String title;\n}<\/code><\/pre>\n<p>\nUnd fast alles ist bereit. Nun liegt es an Hibernate, die komplexen \u201eDetails\u201c zu generieren, wie diese Entit\u00e4t genau in DDL Ihres \u201eDialekts\u201c von SQL definiert wird:<\/p>\n<pre><code class=\"sql\">\tCREATE TABLE book (\n  id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,\n  title VARCHAR(50),\n \n  CONSTRAINT pk_book PRIMARY KEY (id)\n);\n \nCREATE INDEX i_book_title ON book (title);<\/code><\/pre>\n<p>\n\u2026 und wir beginnen, die Anwendung zu starten. Tats\u00e4chlich eine gro\u00dfartige M\u00f6glichkeit, um schnell zu arbeiten und verschiedene Dinge auszuprobieren.<\/p>\n<p>Aber lassen Sie mich. Ich habe geschummelt.<\/p>\n<ul>\n<li>Wird Hibernate wirklich die Definition dieses benannten Prim\u00e4rschl\u00fcssels anwenden?<\/li>\n<li>Wird Hibernate einen Index in TITLE erstellen? \u2013 Ich wei\u00df, dass wir ihn brauchen werden. <\/li>\n<li>Wird Hibernate diesen Schl\u00fcssel definitiv identifizierend in der Identity Specification machen?<\/li>\n<\/ul>\n<p>\nWahrscheinlich nicht. Wenn Sie Ihr Projekt von Grund auf neu entwickeln, ist es immer praktisch, einfach die alte Datenbank abzulehnen und eine neue zu generieren, sobald Sie die ben\u00f6tigten Annotationen hinzugef\u00fcgt haben. So wird die Entit\u00e4t Book letztendlich aussehen: <\/p>\n<pre><code class=\"java\">\t@Entity\n@Table(name = \"book\", indexes = {\n  @Index(name = \"i_book_title\", columnList = \"title\")\n})\nclass Book {\n  @Id\n  @GeneratedValue(strategy = IDENTITY)\n  int id;\n  String title;\n}\n<\/code><\/pre>\n<p>\nCool. Neu generieren. In diesem Fall wird es zu Beginn sehr einfach sein.<\/p>\n<h4>Aber sp\u00e4ter m\u00fcssen Sie daf\u00fcr bezahlen.<\/h4>\n<p>\nFr\u00fcher oder sp\u00e4ter m\u00fcssen Sie in die Produktion gehen. Genau dann wird dieses Modell nicht mehr funktionieren. Denn: <\/p>\n<p>In der Produktion wird es nicht mehr m\u00f6glich sein, die alte Datenbank nach Bedarf abzulehnen und alles von vorne zu beginnen. Ihre Datenbank wird erbbasiert.<\/p>\n<p>Von nun an m\u00fcssen Sie schreiben <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2014\/06\/25\/flyway-and-jooq-for-unbeatable-sql-development-productivity\/\">DDL-Migrationsskripte, zum Beispiel mit Flyway.<\/a><\/noindex>. Was passiert in diesem Fall mit Ihren Entit\u00e4ten? Sie k\u00f6nnen sie entweder manuell anpassen (und dadurch Ihre Arbeitslast verdoppeln), oder Sie lassen Hibernate sie f\u00fcr Sie neu generieren (wie hoch sind die Chancen, dass die so generierte Ihren Erwartungen entspricht?) In jedem Fall verlieren Sie.<\/p>\n<p>So ben\u00f6tigen Sie, sobald Sie in die Produktion gehen, Hotfixes. Und diese m\u00fcssen sehr schnell in die Produktion gebracht werden. Da Sie jedoch nicht vorbereitet sind und keinen reibungslosen Workflow f\u00fcr Ihre Migrationen organisiert haben, patchen Sie alles wild. Und dann schaffen Sie es nicht, alles richtig zu machen. Und beschuldigen Hibernate, denn es ist immer jemand anderes schuld, nur nicht Sie ...<\/p>\n<p>Stattdessen h\u00e4tte man von Anfang an alles ganz anders machen k\u00f6nnen. Zum Beispiel h\u00e4tte man runde R\u00e4der an das Fahrrad setzen k\u00f6nnen.<\/p>\n<h4>Zuerst die Datenbank.<\/h4>\n<p>\nDie wahre \u201eWahrheit\u201c im Schema Ihrer Datenbank und die \u201eSouver\u00e4nit\u00e4t\u201c dar\u00fcber liegen innerhalb der Datenbank. Das Schema wird nur in der Datenbank selbst festgelegt und nirgendwo sonst, und jeder Client hat eine Kopie dieses Schemas, weshalb es v\u00f6llig sinnvoll ist, die Einhaltung des Schemas und dessen Integrit\u00e4t durchzusetzen, und zwar direkt in der Datenbank \u2013 dort, wo die Informationen gespeichert sind. <br \/>\nDas ist eine alte, oft zitierte Weisheit. Prim\u00e4r- und eindeutige Schl\u00fcssel sind gut. Fremdschl\u00fcssel sind gut. Einschr\u00e4nkungspr\u00fcfungen sind gut. <noindex><a rel=\"nofollow\" href=\"https:\/\/community.oracle.com\/ideas\/13028\">Behauptungen<\/a><\/noindex> sind gut. <\/p>\n<p>Au\u00dferdem ist das noch nicht alles. Wenn Sie Oracle verwenden, m\u00f6chten Sie wahrscheinlich angeben:<\/p>\n<ul>\n<li>In welchem Tablespace sich Ihre Tabelle befindet <\/li>\n<li>Was der Wert von PCTFREE ist <\/li>\n<li>Wie gro\u00df der Cache in Ihrer Sequenz (hinter der ID) ist<\/li>\n<\/ul>\n<p>\nVielleicht ist das alles in kleinen Systemen nicht wichtig, aber man muss nicht auf den \u00dcbergang in den Bereich \u201eBig Data\u201c warten \u2013 man kann viel fr\u00fcher von den vom Anbieter bereitgestellten Optimierungsm\u00f6glichkeiten profitieren, wie die oben genannten. Keine der ORM, die ich bisher gesehen habe (auch jOOQ nicht), bietet Zugriff auf die vollst\u00e4ndige Palette von DDL-Optionen, die Sie m\u00f6glicherweise in Ihrer Datenbank verwenden m\u00f6chten. ORM bieten einige Werkzeuge, die dabei helfen, DDL zu schreiben.<\/p>\n<p>Aber letztendlich ist ein gut gestaltetes Schema manuell in DDL geschrieben. Jedes generierte DDL ist lediglich eine Ann\u00e4herung daran.<\/p>\n<h4>Was ist mit dem Client-Modell?<\/h4>\n<p>\nWie oben erw\u00e4hnt, ben\u00f6tigen Sie auf dem Client eine Kopie des Schemas Ihrer Datenbank, eine Client-Darstellung. Es ist \u00fcberfl\u00fcssig zu erw\u00e4hnen, dass diese Client-Darstellung mit dem tats\u00e4chlichen Modell synchronisiert sein muss. Wie erreichen Sie das am besten? Mit einem Code-Generator.<\/p>\n<p>Alle Datenbanken stellen ihre Metainformationen \u00fcber SQL bereit. So erhalten Sie alle Tabellen aus Ihrer Datenbank in verschiedenen SQL-Dialekten:<\/p>\n<pre><code class=\"sql\">\t-- H2, HSQLDB, MySQL, PostgreSQL, SQL Server\nSELECT table_schema, table_name\nFROM information_schema.tables\n \n-- DB2\nSELECT tabschema, tabname\nFROM syscat.tables\n \n-- Oracle\nSELECT owner, table_name\nFROM all_tables\n \n-- SQLite\nSELECT name\nFROM sqlite_master\n \n-- Teradata\nSELECT databasename, tablename\nFROM dbc.tables\n<\/code><\/pre>\n<p>\nDiese Abfragen (oder \u00e4hnliche, je nachdem, ob auch Sichten, materialisierte Sichten, Tabellenwertfunktionen ber\u00fccksichtigt werden m\u00fcssen) werden ebenfalls durch den Aufruf <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.oracle.com\/javase\/10\/docs\/api\/java\/sql\/DatabaseMetaData.html#getTables(java.lang.String,java.lang.String,java.lang.String,java.lang.String%5B%5D)\"><code>DatabaseMetaData.getTables()<\/code><\/a><\/noindex> aus JDBC oder durch das jOOQ-Metamodul ausgef\u00fchrt.<\/p>\n<p>Aus den Ergebnissen solcher Abfragen ist es relativ einfach, jede Client-Darstellung Ihres Datenbankmodells zu generieren, unabh\u00e4ngig davon, welche Technologie Sie auf dem Client verwenden.<\/p>\n<ul>\n<li>Wenn Sie JDBC oder Spring verwenden, k\u00f6nnen Sie eine Menge von String-Konstanten erstellen <\/li>\n<li>Wenn Sie JPA verwenden, k\u00f6nnen Sie die Entit\u00e4ten selbst generieren <\/li>\n<li>Wenn Sie jOOQ verwenden, k\u00f6nnen Sie die jOOQ-Metamodel generieren<\/li>\n<\/ul>\n<p>\nAbh\u00e4ngig von dem Umfang der M\u00f6glichkeiten, die Ihre Client-API (zum Beispiel jOOQ oder JPA) bietet, kann das generierte Metamodell wirklich reichhaltig und vollst\u00e4ndig sein. Nehmen wir zum Beispiel die M\u00f6glichkeit der impliziten Joins, <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2018\/02\/20\/type-safe-implicit-join-through-path-navigation-in-jooq-3-11\/\">die in jOOQ 3.11 eingef\u00fchrt wurde<\/a><\/noindex>, die auf den generierten Metainformationen \u00fcber die Beziehungen der Fremdschl\u00fcssel basiert, die zwischen Ihren Tabellen wirken.<\/p>\n<p>Jetzt wird jede \u00c4nderung der Datenbank automatisch zu einer Aktualisierung des Client-Codes f\u00fchren. Stellen Sie sich zum Beispiel vor:<\/p>\n<pre><code class=\"sql\">ALTER TABLE book RENAME COLUMN title TO book_title;<\/code><\/pre>\n<p>\nW\u00fcrden Sie wirklich diese Arbeit zweimal machen wollen? Auf keinen Fall. Wir halten einfach das DDL fest, f\u00fchren es durch Ihre Build-Pipeline und erhalten die aktualisierte Entit\u00e4t:<\/p>\n<pre><code class=\"java\">@Entity\n@Table(name = \"book\", indexes = {\n \n  \/\/ Haben Sie dar\u00fcber nachgedacht?\n  @Index(name = \"i_book_title\", columnList = \"book_title\")\n})\nclass Book {\n  @Id\n  @GeneratedValue(strategy = IDENTITY)\n  int id;\n \n  @Column(\"book_title\")\n  String bookTitle;\n}<\/code><\/pre>\n<p>\nOder die aktualisierte jOOQ-Klasse. Die meisten DDL-\u00c4nderungen spiegeln sich auch in der Semantik wider und nicht nur in der Syntax. Daher ist es manchmal praktisch, im kompilierten Code zu betrachten, welcher Code durch die \u00c4nderung Ihrer Datenbank betroffen sein wird (oder sein k\u00f6nnte).<\/p>\n<h4>Die einzige Wahrheit<\/h4>\n<p>\nUnabh\u00e4ngig von der Technologie, die Sie verwenden, gibt es immer ein Modell, das die einzige Quelle der Wahrheit f\u00fcr ein bestimmtes Subsystem ist \u2013 oder zumindest sollten wir dies anstreben und vermeiden, eine Art Unternehmensverwirrung zu schaffen, in der \"Wahrheit\" \u00fcberall und nirgendwo ist. Alles k\u00f6nnte viel einfacher sein. Wenn Sie lediglich XML-Dateien mit einem anderen System austauschen, verwenden Sie einfach XSD. Sehen Sie sich das Metamodell INFORMATION_SCHEMA aus jOOQ in XML-Form an:<br \/>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/www.jooq.org\/xsd\/jooq-meta-3.10.0.xsd\">https:\/\/www.jooq.org\/xsd\/jooq-meta-3.10.0.xsd<\/a><\/noindex><\/p>\n<ul>\n<li>XSD ist gut verst\u00e4ndlich<\/li>\n<li>XSD markiert den Inhalt von XML sehr gut und erm\u00f6glicht die Validierung in allen Client-Sprachen <\/li>\n<li>XSD l\u00e4sst sich gut versionieren und hat einen ausgebauten R\u00fcckw\u00e4rtskompatibilit\u00e4tsgrad <\/li>\n<li>XSD kann mit XJC in Java-Code \u00fcbersetzt werden<\/li>\n<\/ul>\n<p>\nDer letzte Punkt ist wichtig. Bei der Kommunikation mit einem externen System \u00fcber XML-Nachrichten m\u00f6chten wir sicherstellen, dass unsere Nachrichten g\u00fcltig sind. Dies l\u00e4sst sich sehr leicht mit JAXB, XJC und XSD erreichen. Es w\u00e4re v\u00f6lliger Wahnsinn zu erwarten, dass man bei einem \u201eJava-first\u201c-Designansatz, bei dem wir unsere Nachrichten als Java-Objekte erstellen, diese klar in XML umwandeln und zur Verwendung in ein anderes System senden kann. XML, das auf diese Weise generiert wird, w\u00e4re von sehr schlechter Qualit\u00e4t, nicht dokumentiert und schwer zu entwickeln. Wenn f\u00fcr diese Schnittstelle eine Vereinbarung \u00fcber die Servicequalit\u00e4t (SLA) existiert, w\u00fcrden wir sie sofort vermasseln.<\/p>\n<p>Ehrlich gesagt passiert genau das st\u00e4ndig mit APIs in JSON, aber das ist eine andere Geschichte, dar\u00fcber werde ich n\u00e4chstes Mal meckern... <\/p>\n<h4>Datenbanken: Das ist alles dasselbe.<\/h4>\n<p>\nWenn Sie mit Datenbanken arbeiten, verstehen Sie, dass alle im Grunde genommen \u00e4hnlich sind. Eine Datenbank besitzt ihre eigenen Daten und muss das Schema verwalten. Alle \u00c4nderungen, die am Schema vorgenommen werden, sollten direkt in DDL umgesetzt werden, um eine einheitliche Quelle der Wahrheit zu gew\u00e4hrleisten.<\/p>\n<p>Sobald die Quelle aktualisiert wurde, m\u00fcssen auch alle Clients ihre Modelle aktualisieren. Einige Clients k\u00f6nnten in Java mit jOOQ und Hibernate oder JDBC (oder alle zusammen) geschrieben werden. Andere Clients k\u00f6nnten in Perl geschrieben sein (da bleibt nur zu w\u00fcnschen, dass ihnen Gl\u00fcck beschieden ist), und wieder andere in C#. Das ist nicht wichtig. Das Hauptmodell befindet sich in der Datenbank. Modelle, die mit ORM generiert werden, sind in der Regel von schlechter Qualit\u00e4t, schlecht dokumentiert und schwer zu entwickeln.<\/p>\n<p>Machen Sie also keine Fehler. Machen Sie von Anfang an keine Fehler. Arbeiten Sie basierend auf der Datenbank. Erstellen Sie eine Bereitstellungspipeline, die automatisiert werden kann. Integrieren Sie Code-Generatoren, um das Modell Ihrer Datenbank bequem zu kopieren und auf die Clients zu \u00fcbertragen. Und h\u00f6ren Sie auf, sich um Code-Generatoren zu sorgen. Sie sind gut. Mit ihnen werden Sie produktiver. Man muss nur von Anfang an etwas Zeit in ihre Einrichtung investieren \u2013 und dann erwarten Sie Jahre erh\u00f6hter Produktivit\u00e4t, aus denen die Geschichte Ihres Projekts entsteht.<\/p>\n<p>Noch nicht bedanken, sp\u00e4ter.<\/p>\n<h4>Erl\u00e4uterung<\/h4>\n<p>\nZur Klarheit: Dieser Artikel propagiert keineswegs, dass das gesamte System (d.h. das Fachgebiet, die Gesch\u00e4ftslogik usw.) der Datenbankmodellierung angepasst werden sollte. In diesem Artikel spreche ich dar\u00fcber, dass der Client-Code, der mit der Datenbank interagiert, von dem Datenbankmodell ausgehen sollte, sodass das Modell der Datenbank nicht selbst als \"erste Klasse\" im Code reproduziert wird. Diese Logik wird normalerweise auf der Datenzugriffsebene in Ihrem Client umgesetzt.<\/p>\n<p>In zweistufigen Architekturen, die an einigen Orten immer noch existieren, kann ein solches Systemmodell das einzig M\u00f6gliche sein. In den meisten Systemen scheint mir jedoch die Datenzugriffsebene eine \"Subsystem\" zu sein, die das Datenbankmodell kapselt.<\/p>\n<h4>Ausnahmen<\/h4>\n<p>\nAus jedem Regel gibt es Ausnahmen, und ich habe bereits erw\u00e4hnt, dass der Ansatz mit der Vorrangstellung der Datenbank und der Generierung von Quellcode manchmal ungeeignet sein kann. Hier sind ein paar solcher Ausnahmen (vielleicht findet sich noch mehr):<\/p>\n<ul>\n<li>Wenn das Schema unbekannt ist und es ge\u00f6ffnet werden muss. Zum Beispiel, wenn Sie ein Anbieter von Tools sind, die Benutzern helfen, sich in jedem Schema zurechtzufinden. Uff. Hier kommt man ohne Codegenerierung nicht aus. Aber trotzdem \u2013 die Datenbank steht an erster Stelle.<\/li>\n<li>Wenn das Schema zur Laufzeit f\u00fcr die L\u00f6sung einer bestimmten Aufgabe generiert werden muss. Dieses Beispiel erscheint mir als eine etwas beeindruckende Version des Musters <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Entity%E2%80%93attribute%E2%80%93value_model\">entity attribute value<\/a><\/noindex>, d.h., Sie haben tats\u00e4chlich kein klar definiertes Schema. In diesem Fall kann man oft nicht einmal sicher sein, ob Ihnen ein RDBMS zusagt.<\/li>\n<\/ul>\n<p>\nAusnahmen sind ihrer Natur nach au\u00dfergew\u00f6hnlich. In den meisten F\u00e4llen, die mit der Nutzung von RDBMS verbunden sind, ist das Schema im Voraus bekannt, es befindet sich innerhalb des RDBMS und stellt die einzige Quelle der \"Wahrheit\" dar, w\u00e4hrend alle Clients Abk\u00f6mmlinge davon herstellen m\u00fcssen. Idealerweise sollte dabei ein Codegenerator verwendet werden.<br \/>\n<br \/>Quelle: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/piter\/blog\/498816\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u041c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u043c\u0443 Java \u0438 Spring, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435, \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0431\u0430\u0437 \u0434\u0430\u043d\u043d\u044b\u0445. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u043c \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u043e \u0442\u043e\u043c, \u043f\u043e\u0447\u0435\u043c\u0443 \u043f\u0440\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0438\u043c\u0435\u043d\u043d\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u0430 \u043d\u0435 \u043a\u043e\u0434 Java, \u0434\u043e\u043b\u0436\u043d\u0430 \u0438\u043c\u0435\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u043a\u0430\u043a \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f, \u0438 \u043a\u0430\u043a\u0438\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0435\u0441\u0442\u044c \u0438\u0437 \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u0430. \u0412 \u044d\u0442\u043e\u0439 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0437\u0430\u043f\u043e\u0437\u0434\u0430\u043b\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043e\u0431\u044a\u044f\u0441\u043d\u044e, [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":79466,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-79465","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u041c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u043c\u0443\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"de_DE\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0418\u0441\u0442\u0438\u043d\u0430 \u043f\u0440\u0435\u0436\u0434\u0435 \u0432\u0441\u0435\u0433\u043e, \u0438\u043b\u0438 \u043f\u043e\u0447\u0435\u043c\u0443 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u041c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u043c\u0443\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-04-27T05:42:20+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-04-27T05:42:20+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Wahrheit an erster Stelle oder warum Systeme auf Grundlage der Datenbankstruktur entworfen werden m\u00fcssen | ProHoster","description":"Hallo, Habr! Wir setzen unsere Erkundung des Themas fort","canonical_url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"de_DE","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0418\u0441\u0442\u0438\u043d\u0430 \u043f\u0440\u0435\u0436\u0434\u0435 \u0432\u0441\u0435\u0433\u043e, \u0438\u043b\u0438 \u043f\u043e\u0447\u0435\u043c\u0443 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 | ProHoster","og:description":"\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u041c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u043c\u0443","og:url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-04-27T05:42:20+00:00","article:modified_time":"2020-04-27T05:42:20+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"79465","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 16:37:24","updated":"2022-09-29 16:13:15","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/79465","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/comments?post=79465"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/79465\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media\/79466"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media?parent=79465"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/categories?post=79465"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/tags?post=79465"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}