{"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\/sv\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh","title":{"rendered":"Sanningen f\u00f6rst, eller varf\u00f6r systemet m\u00e5ste designas utifr\u00e5n databasens struktur","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Hej, Habr!<\/p>\n<p>Vi forts\u00e4tter att utforska \u00e4mnet <noindex><a rel=\"nofollow\" href=\"https:\/\/www.piter.com\/collection\/all\/product\/sovremennyy-yazyk-java-lyambda-vyrazheniya-potoki-i-funktsionalnoe-programmirovanie\">Java<\/a><\/noindex> och <noindex><a rel=\"nofollow\" href=\"https:\/\/storage.piter.com\/upload\/contents\/978544611587\/978544611587_X.pdf\">Spring<\/a><\/noindex>, inklusive p\u00e5 databaseniv\u00e5. Idag erbjuder vi att l\u00e4sa om varf\u00f6r databassstruktur, snarare \u00e4n Java-kod, b\u00f6r ha avg\u00f6rande betydelse n\u00e4r man designar stora applikationer, hur detta g\u00f6rs och vilka undantag som finns fr\u00e5n denna regel.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex> <br \/>\nI den h\u00e4r ganska sena artikeln kommer jag att f\u00f6rklara varf\u00f6r jag anser att data-modellen i en applikation i praktiskt taget alla fall b\u00f6r designas \"utifr\u00e5n databasen\", snarare \u00e4n \"utifr\u00e5n Java-m\u00f6jligheterna\" (eller n\u00e5got annat klient-spr\u00e5k som du arbetar med). V\u00e4ljer man den andra metoden, d\u00e5 ger man sig in p\u00e5 en l\u00e5ng v\u00e4g av sm\u00e4rta och lidande, s\u00e5 snart ditt projekt b\u00f6rjar v\u00e4xa.<\/p>\n<p>Artikeln \u00e4r skriven med inspiration fr\u00e5n <noindex><a rel=\"nofollow\" href=\"https:\/\/stackoverflow.com\/questions\/50706556\/kotlin-with-jooq-and-write-table-models-manually-without-code-generation\">en fr\u00e5ga<\/a><\/noindex>, st\u00e4lld p\u00e5 Stack Overflow.<\/p>\n<p>Intressanta diskussioner p\u00e5 reddit i sektionerna <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> och <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>Kodgenerering<\/h4>\n<p>\nJag blev verkligen f\u00f6rv\u00e5nad \u00f6ver att det finns en s\u00e5 liten skara anv\u00e4ndare som, efter att ha bekantat sig med jOOQ, blir uppr\u00f6rda \u00f6ver det faktum att jOOQ allvarligt baseras p\u00e5 kodgenerering. Ingen hindrar dig fr\u00e5n att anv\u00e4nda jOOQ som du tycker \u00e4r n\u00f6dv\u00e4ndigt, och ingen tvingar dig att anv\u00e4nda kodgenerering. Men som standard (som beskrivs i manualen) fungerar jOOQ s\u00e5 h\u00e4r: du b\u00f6rjar med (\u00e4rvda) databasscheman, utf\u00f6r omv\u00e4nd ingenj\u00f6rskonst med hj\u00e4lp av jOOQs kodgenerator f\u00f6r att p\u00e5 s\u00e5 s\u00e4tt f\u00e5 en upps\u00e4ttning klasser som representerar dina tabeller, och sedan skriver du typ-s\u00e4kra fr\u00e5gor till dessa tabeller:<\/p>\n<pre><code class=\"java\">\tfor (Record2 record : DSL.using(configuration) \n\/\/   ^^^^^^^^^^^^^^^^^^^^^^^ Typinformation har h\u00e4rletts fr\u00e5n \n\/\/   den genererade koden vilket refereras av det \n\/\/   f\u00f6ljande SELECT-villkoret \n \n       .select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME) \n\/\/           vvvvv ^^^^^^^^^^^^  ^^^^^^^^^^^^^^^ genererade namn\n       .from(ACTOR) \n       .orderBy(1, 2)) { \n    \/\/ ...\n}<\/code><\/pre>\n<p>\nKoden genereras antingen manuellt utanf\u00f6r sammans\u00e4ttningen, eller manuellt vid varje sammans\u00e4ttning. Till exempel kan s\u00e5dan regenerering f\u00f6lja direkt efter <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2014\/06\/25\/flyway-and-jooq-for-unbeatable-sql-development-productivity\/\">Flyway-databasmigrationen, som ocks\u00e5 kan utf\u00f6ras manuellt eller automatiskt.<\/a><\/noindex>.<\/p>\n<h4>Kodgenerering<\/h4>\n<p>\nMed s\u00e5dana metoder f\u00f6r kodgenerering \u2013 manuella och automatiska \u2013 \u00e4r knutna olika filosofier, f\u00f6rdelar och nackdelar som jag inte avser att diskutera i detalj i denna artikel. Men i allm\u00e4nhet handlar hela essensen av genererad kod om att \u00e5terge den 'sanning' vi accepterar som en given, antingen inom v\u00e5rt system eller utanf\u00f6r det. P\u00e5 ett s\u00e4tt g\u00f6r kompilatorer samma sak, som genererar bytekod, maskinkod eller n\u00e5gon annan typ av kod baserat p\u00e5 k\u00e4llkod \u2013 vi f\u00e5r en representation av v\u00e5r 'sanning' p\u00e5 ett annat spr\u00e5k, oavsett specifika orsaker. <\/p>\n<p>Det finns m\u00e5nga s\u00e5dana kodgeneratorer. Till exempel, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/technotes\/tools\/unix\/xjc.html\">XJC kan generera Java-kod baserat p\u00e5 XSD- eller WSDL-filer.<\/a><\/noindex>Principen \u00e4r alltid densamma:<\/p>\n<ul>\n<li>Det finns en viss sanning (intern eller extern) \u2013 till exempel specifikation, datamodell, osv.<\/li>\n<li>Vi beh\u00f6ver en lokal representation av denna sanning p\u00e5 v\u00e5rt programmeringsspr\u00e5k.<\/li>\n<\/ul>\n<p>\nOch det \u00e4r n\u00e4stan alltid f\u00f6rnuftigt att generera en s\u00e5dan representation \u2013 f\u00f6r att undvika \u00f6verfl\u00f6dighet.<\/p>\n<h4>Typleverant\u00f6rer och bearbetning av annoteringar<\/h4>\n<p>\nObservera: en annan, modernare och mer specifik metod f\u00f6r kodgenerering f\u00f6r jOOQ \u00e4r kopplad till anv\u00e4ndningen av typleverant\u00f6rer, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/fsharp\/tutorials\/type-providers\/\">i den form de implementeras i F#.<\/a><\/noindex>I s\u00e5dana fall genereras koden av kompilatorn, faktiskt vid kompileringstillf\u00e4llet. I form av k\u00e4llkod existerar s\u00e5dan kod inte egentligen. I Java finns liknande, om \u00e4n inte s\u00e5d\u00e4r eleganta verktyg \u2013 det \u00e4r annoteringsprocessorer, till exempel, <noindex><a rel=\"nofollow\" href=\"https:\/\/projectlombok.org\">Lombok.<\/a><\/noindex>.<\/p>\n<p>P\u00e5 s\u00e4tt och vis h\u00e4nder samma saker h\u00e4r som i det f\u00f6rsta fallet, med undantag f\u00f6r att:<\/p>\n<ul>\n<li>Du ser inte den genererade koden (m\u00f6jligen verkar denna situation inte s\u00e5 avskr\u00e4ckande f\u00f6r n\u00e5gon?)<\/li>\n<li>Du m\u00e5ste garantera att typer kan erbjudas, vilket betyder att 'sanningen' alltid m\u00e5ste vara tillg\u00e4nglig. Detta \u00e4r l\u00e4tt i fallet med Lombok, som annoterar \"sanningen\". Lite mer komplicerat \u00e4r det med databasmodeller, vars funktioner beror p\u00e5 att en st\u00e4ndig aktiv anslutning finns tillg\u00e4nglig.<\/li>\n<\/ul>\n<p><\/p>\n<h4>Vad \u00e4r problemet med kodgenerering?<\/h4>\n<p>\nF\u00f6rutom den knepiga fr\u00e5gan om hur man b\u00e4st ska starta kodgenereringen \u2013 manuellt eller automatiskt \u2013 m\u00e5ste man ocks\u00e5 n\u00e4mna att det finns personer som anser att kodgenerering \u00f6verhuvudtaget inte beh\u00f6vs. Den mest frekventa motiveringen f\u00f6r denna synpunkt som jag har st\u00f6tt p\u00e5 \u00e4r att det d\u00e5 \u00e4r sv\u00e5rt att konfigurera byggpipeline. Ja, det \u00e4r verkligen sv\u00e5rt. Det uppst\u00e5r ytterligare infrastrukturkostnader. Om du just har b\u00f6rjat arbeta med en viss produkt (oavsett om det \u00e4r jOOQ, JAXB, Hibernate, etc.), tar det tid att st\u00e4lla in arbetsmilj\u00f6n, en tid som du hellre skulle l\u00e4gga p\u00e5 att studera API:et och s\u00e5 sm\u00e5ningom dra nytta av det.<\/p>\n<p>Om kostnaderna f\u00f6r att f\u00f6rst\u00e5 hur generatorn fungerar \u00e4r f\u00f6r h\u00f6ga \u2013 d\u00e5 har API:et verkligen inte arbetat tillr\u00e4ckligt med anv\u00e4ndarv\u00e4nligheten f\u00f6r kodgeneratorn (och det visar sig senare att \u00e4ven anv\u00e4ndarinst\u00e4llningar \u00e4r sv\u00e5ra). Anv\u00e4ndarv\u00e4nlighet b\u00f6r alltid vara h\u00f6gsta prioritet f\u00f6r ett s\u00e5dant API. Men det h\u00e4r \u00e4r bara ett argument mot kodgenerering. I \u00f6vrigt \u00e4r det helt och h\u00e5llet n\u00f6dv\u00e4ndigt att skriva en lokal representation av interna eller externa sanningar f\u00f6r hand.<\/p>\n<p>M\u00e5nga kommer att s\u00e4ga att de inte har tid f\u00f6r detta. De har strikta deadlines f\u00f6r sin Super-Produkt. Senare f\u00e5r vi uppdatera byggpipelines, det finns tid. Jag responderar:<\/p>\n<p><img decoding=\"async\" alt=\"Sanningen f\u00f6rst, eller varf\u00f6r systemet m\u00e5ste designas utifr\u00e5n databasens struktur\" 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&#8217;Rourke, Audience Stack<\/a><\/noindex> <\/p>\n<p><b>Men i Hibernate \/ JPA \u00e4r det s\u00e5 enkelt att skriva kod \"f\u00f6r Java\". <\/b><\/p>\n<p>Verkligen. F\u00f6r Hibernate och dess anv\u00e4ndare \u00e4r detta b\u00e5de en v\u00e4lsignelse och en f\u00f6rbannelse. I Hibernate kan man enkelt skriva ett par entiteter, s\u00e5 h\u00e4r:<\/p>\n<pre><code class=\"java\">\t@Entity\nclass Book {\n  @Id\n  int id;\n  String title;\n}<\/code><\/pre>\n<p>\nOch n\u00e4stan allt \u00e4r klart. Nu \u00e4r det Hibernates uppgift \u2013 att generera komplexa \"detaljer\" om hur just denna entitet kommer att definieras i DDL f\u00f6r din \"dialekt\" SQL:<\/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 och vi b\u00f6rjar k\u00f6ra applikationen. Verkligen fantastisk m\u00f6jlighet att snabbt komma ig\u00e5ng och testa olika saker.<\/p>\n<p>Men v\u00e4nta. Jag har \u00f6verdrivit.<\/p>\n<ul>\n<li>Kommer Hibernate verkligen att anv\u00e4nda definitionen av denna namngivna prim\u00e4ra nyckel?<\/li>\n<li>Kommer Hibernate att skapa ett index p\u00e5 TITLE? \u2013 jag vet att vi kommer att beh\u00f6va det. <\/li>\n<li>Kommer Hibernate verkligen att g\u00f6ra denna nyckel identifierande i Identity Specification?<\/li>\n<\/ul>\n<p>\nTroligen inte. Om du utvecklar ditt projekt fr\u00e5n grunden, \u00e4r det alltid praktiskt att bara sl\u00e4ppa den gamla databasen och generera en ny s\u00e5 snart du har lagt till n\u00f6dv\u00e4ndiga annoteringar. S\u00e5, entiteten Book kommer s\u00e5 sm\u00e5ningom att se ut s\u00e5 h\u00e4r: <\/p>\n<pre><code class=\"java\">\t@Entity\n@Table(name = &quot;book&quot;, indexes = {\n  @Index(name = &quot;i_book_title&quot;, columnList = &quot;title&quot;)\n})\nclass Book {\n  @Id\n  @GeneratedValue(strategy = IDENTITY)\n  int id;\n  String title;\n}\n<\/code><\/pre>\n<p>\nCoolt. Generera om. \u00c5terigen, i s\u00e5 fall kommer det att bli mycket enkelt i b\u00f6rjan.<\/p>\n<h4>Men senare kommer du att f\u00e5 betala f\u00f6r det.<\/h4>\n<p>\nF\u00f6rr eller senare m\u00e5ste du g\u00e5 i produktion. Just d\u00e5 kommer denna modell att sluta fungera. F\u00f6r att: <\/p>\n<p>I produktion kan du inte l\u00e4ngre kasta bort den gamla databasen och b\u00f6rja om fr\u00e5n b\u00f6rjan. Din databas kommer att bli en arvtagare.<\/p>\n<p>Fr\u00e5n och med nu och f\u00f6r alltid m\u00e5ste du skriva <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2014\/06\/25\/flyway-and-jooq-for-unbeatable-sql-development-productivity\/\">DDL-migreringsskript, till exempel med Flyway.<\/a><\/noindex>. Och vad h\u00e4nder i s\u00e5 fall med dina entiteter? Du kan antingen anpassa dem manuellt (och p\u00e5 s\u00e5 s\u00e4tt dubbla din arbetsbelastning), eller be Hibernate generera dem igen \u00e5t dig (hur stora \u00e4r chanserna att det som genereras p\u00e5 detta s\u00e4tt motsvarar dina f\u00f6rv\u00e4ntningar?) Du f\u00f6rlorar oavsett.<\/p>\n<p>D\u00e4rf\u00f6r, s\u00e5 snart du g\u00e5r i produktion, beh\u00f6ver du snabba patchar. Och de m\u00e5ste sl\u00e4ppas i produktion mycket snabbt. Eftersom du inte var f\u00f6rberedd och inte organiserade en smidig process f\u00f6r dina migrationer till produktion, kommer du att patcha \u00f6verallt. Och sedan hinner du inte g\u00f6ra allt korrekt. Och skyller p\u00e5 Hibernate, eftersom alltid n\u00e5gon annan \u00e4r skyldig, utom du\u2026<\/p>\n<p>Ist\u00e4llet kunde allt ha gjorts helt annorlunda fr\u00e5n b\u00f6rjan. Till exempel, s\u00e4tta p\u00e5 cykeln runda hjul.<\/p>\n<h4>F\u00f6rst databasen.<\/h4>\n<p>\nDen verkliga \"sanningen\" i strukturen f\u00f6r din databas och \"suver\u00e4niteten\" \u00f6ver den finns inne i databasen. Strukturen definieras endast i sj\u00e4lva databasen och ingen annanstans, och varje kund har en kopia av denna struktur, s\u00e5 det \u00e4r helt rimligt att f\u00f6reskriva \u00f6verensst\u00e4mmelse med strukturen och dess integritet, g\u00f6ra det direkt i databasen \u2013 d\u00e4r informationen lagras. <br \/>\nDetta \u00e4r en gammal och till och med slitstark visdom. Prim\u00e4ra och unika nycklar \u00e4r bra. Utlandsnycklar \u2013 bra. Kontroller av begr\u00e4nsningar \u2013 bra. <noindex><a rel=\"nofollow\" href=\"https:\/\/community.oracle.com\/ideas\/13028\">P\u00e5st\u00e5enden<\/a><\/noindex> \u2013 bra. <\/p>\n<p>Dessutom \u00e4r detta inte allt. Om du anv\u00e4nder Oracle, vill du troligen specificera:<\/p>\n<ul>\n<li>I vilket tablespace din tabell finns <\/li>\n<li>Vad dess PCTFREE-v\u00e4rde \u00e4r <\/li>\n<li>Vad storleken p\u00e5 cachen i din sekvens (efter identifieraren) \u00e4r<\/li>\n<\/ul>\n<p>\nDetta kanske inte betyder s\u00e5 mycket i mindre system, men du beh\u00f6ver inte v\u00e4nta med att g\u00e5 \u00f6ver till \"big data\"-omr\u00e5det \u2014 du kan b\u00f6rja dra nytta av de lagringsoptimeringar som leverant\u00f6ren erbjuder mycket tidigare, som de ovan n\u00e4mnda. Ingen av de ORM:er jag har sett (inklusive jOOQ) ger \u00e5tkomst till hela upps\u00e4ttningen av DDL-alternativ som du kanske vill anv\u00e4nda i din databas. ORM:er erbjuder vissa verktyg som hj\u00e4lper till att skriva DDL.<\/p>\n<p>Men slutligen \u00e4r en v\u00e4l utformad struktur skriven f\u00f6r hand i DDL. All genererad DDL \u00e4r bara en approximation av den.<\/p>\n<h4>Vad h\u00e4nder med klientmodellen?<\/h4>\n<p>\nSom tidigare n\u00e4mnts kommer du att beh\u00f6va en kopia av din databasstruktur p\u00e5 klienten, ett klientvisning. Det \u00e4r \u00f6verfl\u00f6digt att n\u00e4mna att detta klientvisning m\u00e5ste synkroniseras med den faktiska modellen. Hur g\u00f6r man detta p\u00e5 b\u00e4sta s\u00e4tt? Genom en kodgenerator.<\/p>\n<p>Alla databaser tillhandah\u00e5ller sin metadata genom SQL. H\u00e4r \u00e4r hur du f\u00e5r alla tabeller fr\u00e5n din databas p\u00e5 olika SQL-dialekter:<\/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>\nDessa fr\u00e5gor (eller liknande, beroende p\u00e5 om vyer, materialiserade vyer och fuktiga funktioner ocks\u00e5 beaktas) genomf\u00f6rs ocks\u00e5 med ett anrop <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> fr\u00e5n JDBC, eller med hj\u00e4lp av jOOQ:s meta-modul.<\/p>\n<p>Fr\u00e5n resultaten av s\u00e5dana fr\u00e5gor \u00e4r det relativt enkelt att generera vilket klientvisning av din databasmodell som helst, oavsett vilken teknik du anv\u00e4nder p\u00e5 klienten.<\/p>\n<ul>\n<li>Om du anv\u00e4nder JDBC eller Spring, kan du skapa en upps\u00e4ttning str\u00e4ngkonstanter <\/li>\n<li>Om du anv\u00e4nder JPA, kan du generera sj\u00e4lva entiteterna <\/li>\n<li>Om du anv\u00e4nder jOOQ, kan du generera jOOQ:s meta-modell<\/li>\n<\/ul>\n<p>\nBeroende p\u00e5 vilken m\u00e4ngd funktioner som erbjuds av ditt klient-API (t.ex. jOOQ eller JPA), kan den genererade meta-modellen vara verkligt rik och komplett. L\u00e5t oss ta en titt p\u00e5 m\u00f6jligheten f\u00f6r implicita sammanfogningar, <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2018\/02\/20\/type-safe-implicit-join-through-path-navigation-in-jooq-3-11\/\">som tillkom i jOOQ 3.11<\/a><\/noindex>, vilken bygger p\u00e5 den genererade metainformationen om relationerna mellan dina tabeller.<\/p>\n<p>Nu kommer varje \u00e4ndring av databasen automatiskt att leda till en uppdatering av klientkoden. F\u00f6rest\u00e4ll dig till exempel:<\/p>\n<pre><code class=\"sql\">ALTER TABLE book RENAME COLUMN title TO book_title;<\/code><\/pre>\n<p>\nVill du verkligen g\u00f6ra det h\u00e4r arbetet tv\u00e5 g\u00e5nger? Absolut inte. Vi registrerar bara DDL, k\u00f6r det genom din byggpipeline och f\u00e5r den uppdaterade entiteten:<\/p>\n<pre><code class=\"java\">@Entity\n@Table(name = &quot;book&quot;, indexes = {\n \n  \/\/ Har du t\u00e4nkt p\u00e5 det h\u00e4r?\n  @Index(name = &quot;i_book_title&quot;, columnList = &quot;book_title&quot;)\n})\nclass Book {\n  @Id\n  @GeneratedValue(strategy = IDENTITY)\n  int id;\n \n  @Column(&quot;book_title&quot;)\n  String bookTitle;\n}<\/code><\/pre>\n<p>\nEller den uppdaterade jOOQ-klassen. De flesta DDL-f\u00f6r\u00e4ndringar \u00e5terspeglas ocks\u00e5 i semantiken snarare \u00e4n bara i syntaxen. D\u00e4rf\u00f6r kan det vara praktiskt att titta i den kompilerade koden f\u00f6r att se vilken kod som kommer (eller kan) ber\u00f6ras av \u00e4ndringen av din databas.<\/p>\n<h4>Den enda sanningen<\/h4>\n<p>\nOavsett vilken teknologi du anv\u00e4nder, finns det alltid en modell som \u00e4r den enda sanningsk\u00e4llan f\u00f6r en viss subsystem \u2013 eller \u00e5tminstone b\u00f6r vi str\u00e4va efter detta och undvika den d\u00e4r f\u00f6retagsf\u00f6rvirringen, d\u00e4r \"sanningen\" \u00e4r b\u00e5de \u00f6verallt och ingenstans. Allt kan vara mycket enklare. Om du bara utbyter XML-filer med n\u00e5got annat system, anv\u00e4nd d\u00e5 bara XSD. Titta p\u00e5 meta-modellen INFORMATION_SCHEMA fr\u00e5n jOOQ i XML-form:<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 \u00e4r l\u00e4tt att f\u00f6rst\u00e5<\/li>\n<li>XSD strukturerar XML-inneh\u00e5llet mycket v\u00e4l och m\u00f6jligg\u00f6r validering p\u00e5 alla klientspr\u00e5k <\/li>\n<li>XSD versioneras v\u00e4l och har bra bak\u00e5tkompatibilitet <\/li>\n<li>XSD kan \u00f6vers\u00e4ttas till Java-kod med hj\u00e4lp av XJC<\/li>\n<\/ul>\n<p>\nDen sista punkten \u00e4r viktig. N\u00e4r vi kommunicerar med ett externt system med hj\u00e4lp av XML-meddelanden vill vi vara s\u00e4kra p\u00e5 att v\u00e5ra meddelanden \u00e4r giltiga. Detta kan l\u00e4tt uppn\u00e5s med JAXB, XJC och XSD. Det skulle vara rent galet att tro att, med en \"Java-f\u00f6rst\"-design d\u00e4r vi skapar v\u00e5ra meddelanden som Java-objekt, kan dessa p\u00e5 n\u00e5got s\u00e4tt tydligt avbildas p\u00e5 XML och skickas f\u00f6r konsumtion i ett annat system. XML som genereras p\u00e5 detta s\u00e4tt skulle vara av mycket d\u00e5lig kvalitet, icke-dokumenterat och sv\u00e5rt att underh\u00e5lla. Om det fanns ett avtal om serviceniv\u00e5er (SLA) f\u00f6r ett s\u00e5dant gr\u00e4nssnitt skulle vi omedelbart ha krossat det.<\/p>\n<p>\u00c4rligt talat, det h\u00e4r h\u00e4nder st\u00e4ndigt med JSON-API:er, men det \u00e4r en annan historia, n\u00e4sta g\u00e5ng ska jag klaga\u2026 <\/p>\n<h4>Databaser: det \u00e4r samma sak<\/h4>\n<p>\nN\u00e4r du arbetar med databaser f\u00f6rst\u00e5r du att de alla, i princip, liknar varandra. En databas \u00e4ger sina egna data och ska styra schemat. Eventuella \u00e4ndringar som g\u00f6rs i schemat m\u00e5ste genomf\u00f6ras direkt p\u00e5 DDL f\u00f6r att uppdatera den enda sanningsk\u00e4llan.<\/p>\n<p>N\u00e4r uppdateringen av k\u00e4llan har skett m\u00e5ste alla klienter ocks\u00e5 uppdatera sina kopior av modellen. Vissa klienter kan vara skrivna i Java med jOOQ och Hibernate eller JDBC (eller alla p\u00e5 en g\u00e5ng). Andra klienter kan vara skrivna i Perl (jag \u00f6nskar dem lycka till), tredje p\u00e5 C#. Det spelar ingen roll. Huvudmodellen finns i databasen. Modeller som genereras med hj\u00e4lp av ORM \u00e4r vanligtvis av d\u00e5lig kvalitet, d\u00e5ligt dokumenterade och sv\u00e5ra att utveckla.<\/p>\n<p>S\u00e5 g\u00f6r inga misstag. G\u00f6r inga misstag fr\u00e5n b\u00f6rjan. Arbeta utifr\u00e5n databasen. Bygg en distributionspipeline som kan automatiseras. Inkludera kodgeneratorer f\u00f6r att enkelt kopiera modellen av din databas och \u00f6verf\u00f6ra den till klienterna. Sluta oroa dig f\u00f6r kodgeneratorer. De \u00e4r bra. Med dem kommer du att bli mer produktiv. Det kr\u00e4vs bara lite tid fr\u00e5n b\u00f6rjan f\u00f6r att konfigurera dem \u2013 och sedan v\u00e4ntar \u00e5r av \u00f6kad produktivitet som kommer att v\u00e4va din projekthistoria.<\/p>\n<p>Tacka mig inte \u00e4n, senare.<\/p>\n<h4>F\u00f6rklaring<\/h4>\n<p>\nF\u00f6r tydlighetens skull: Denna artikel propagerar p\u00e5 inget s\u00e4tt f\u00f6r att hela systemet (det vill s\u00e4ga \u00e4mnesomr\u00e5det, aff\u00e4rslogiken, etc.) ska anpassas efter din databasmodell. I denna artikel diskuterar jag hur klientkoden som interagerar med databasen b\u00f6r fungera utifr\u00e5n databasmodellen, s\u00e5 att sj\u00e4lva klientkoden inte reproducerar databasmodellen som \"f\u00f6rsta klass\". Denna logik placeras vanligtvis p\u00e5 dat\u00e5tkomstniv\u00e5n hos dig p\u00e5 klienten.<\/p>\n<p>I tv\u00e5niv\u00e5arkitekturer, som fortfarande finns kvar p\u00e5 vissa st\u00e4llen, kan en s\u00e5dan systemmodell vara den enda m\u00f6jliga. Men i de flesta system verkar dat\u00e5tkomstniv\u00e5n f\u00f6r mig som en \"undersystem\" som inkapslar databasmodellen.<\/p>\n<h4>Undantag<\/h4>\n<p>\nDet finns undantag fr\u00e5n varje regel, och jag har redan n\u00e4mnt att den prim\u00e4ra databastillv\u00e4gag\u00e5ngss\u00e4ttet och koden som genereras ibland kan vara ol\u00e4mpliga. H\u00e4r \u00e4r ett par s\u00e5dana undantag (det troligen finns fler):<\/p>\n<ul>\n<li>N\u00e4r schemat \u00e4r ok\u00e4nt och beh\u00f6ver \u00f6ppnas. Till exempel, om du \u00e4r en leverant\u00f6r av verktyg som hj\u00e4lper anv\u00e4ndare att navigera i vilket schema som helst. Puh. H\u00e4r beh\u00f6vs ingen kodgenerering. Men oavsett \u2013 databasen f\u00f6rst.<\/li>\n<li>N\u00e4r schemat m\u00e5ste genereras dynamiskt f\u00f6r att l\u00f6sa ett specifikt problem. Detta exempel verkar vara en n\u00e5got \u00f6verdriven version av m\u00f6nstret <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Entity%E2%80%93attribute%E2%80%93value_model\">entity attribute value<\/a><\/noindex>, det vill s\u00e4ga, du har faktiskt ingen definierad modell. I s\u00e5dana fall kan det ofta vara sv\u00e5rt att vara s\u00e4ker p\u00e5 att en RDBMS passar dina behov.<\/li>\n<\/ul>\n<p>\nUndantag \u00e4r per definition undantagsfall. I de flesta fall som involverar anv\u00e4ndningen av RDBMS \u00e4r schemat k\u00e4nt p\u00e5 f\u00f6rhand, det finns inuti RDBMS och \u00e4r den enda k\u00e4llan till \"sanning\", och alla klienter m\u00e5ste ha kopior som \u00e4r derivat av det. Idealiskt b\u00f6r en kodgenerator anv\u00e4ndas.<br \/>\n<br \/>K\u00e4lla: <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.4 - 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\/sv\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.4\" \/>\n\t\t<meta property=\"og:locale\" content=\"sv_SE\" \/>\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\/sv\/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\udd47Sanningen framf\u00f6r allt, eller varf\u00f6r systemet m\u00e5ste utformas utifr\u00e5n databasens struktur | ProHoster","description":"Hej, Habr! Vi forts\u00e4tter att utforska \u00e4mnet","canonical_url":"https:\/\/prohoster.info\/sv\/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":"sv_SE","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\/sv\/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\/sv\/wp-json\/wp\/v2\/posts\/79465","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/comments?post=79465"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/posts\/79465\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/media\/79466"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/media?parent=79465"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/categories?post=79465"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/tags?post=79465"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}