{"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\/ro\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh","title":{"rendered":"Adev\u0103rul \u00eenainte de toate, sau de ce trebuie s\u0103 proiect\u0103m sistemul pe baza dispozitivului bazei de date","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Salut, Habr!<\/p>\n<p>Continu\u0103m s\u0103 explor\u0103m subiectul <noindex><a rel=\"nofollow\" href=\"https:\/\/www.piter.com\/collection\/all\/product\/sovremennyy-yazyk-java-lyambda-vyrazheniya-potoki-i-funktsionalnoe-programmirovanie\">Java<\/a><\/noindex> \u0219i <noindex><a rel=\"nofollow\" href=\"https:\/\/storage.piter.com\/upload\/contents\/978544611587\/978544611587_X.pdf\">Spring<\/a><\/noindex>, inclusiv la nivelul bazelor de date. Ast\u0103zi, v\u0103 propunem s\u0103 citi\u021bi despre motivul pentru care, \u00een proiectarea aplica\u021biilor mari, structura bazei de date, \u0219i nu codul Java, ar trebui s\u0103 aib\u0103 o importan\u021b\u0103 decisiv\u0103, cum se realizeaz\u0103 acest lucru \u0219i ce excep\u021bii exist\u0103 de la aceast\u0103 regul\u0103.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex> <br \/>\n\u00cen acest articol destul de \u00eent\u00e2rziat, voi explica de ce consider c\u0103, \u00een aproape toate cazurile, modelul de date al unei aplica\u021bii ar trebui s\u0103 fie proiectat \u00ab\u00een baza bazei de date\u00bb, mai degrab\u0103 dec\u00e2t \u00ab\u00een func\u021bie de capabilit\u0103\u021bile Java\u00bb (sau alt limbaj client cu care lucra\u021bi). Aleg\u00e2nd a doua abordare, v\u0103 angaja\u021bi pe un drum lung de durere \u0219i suferin\u021b\u0103, de \u00eendat\u0103 ce proiectul dumneavoastr\u0103 \u00eencepe s\u0103 creasc\u0103.<\/p>\n<p>Articolul este scris pe baza <noindex><a rel=\"nofollow\" href=\"https:\/\/stackoverflow.com\/questions\/50706556\/kotlin-with-jooq-and-write-table-models-manually-without-code-generation\">unei \u00eentreb\u0103ri<\/a><\/noindex>, adresate pe Stack Overflow.<\/p>\n<p>Discu\u021bii interesante pe reddit \u00een sec\u021biunile <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> \u0219i <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>Generarea codului<\/h4>\n<p>\nC\u00e2t de surprins am fost s\u0103 descop\u0103r c\u0103 exist\u0103 o comunitate at\u00e2t de mic\u0103 de utilizatori care, dup\u0103 ce s-au familiarizat cu jOOQ, se revolt\u0103 \u00eempotriva faptului c\u0103, atunci c\u00e2nd lucra\u021bi cu jOOQ, se bazeaz\u0103 serios pe generarea de cod surs\u0103. Nimeni nu v\u0103 opre\u0219te s\u0103 utiliza\u021bi jOOQ a\u0219a cum considera\u021bi necesar \u0219i nu v\u0103 oblig\u0103 s\u0103 folosi\u021bi generarea de cod. Dar \u00een mod implicit (a\u0219a cum este descris \u00een documenta\u021bie), lucrul cu jOOQ se desf\u0103\u0219oar\u0103 astfel: \u00eencepe\u021bi cu o schem\u0103 (mo\u0219tenit\u0103) a bazei de date, efectua\u021bi inversarea proiect\u0103rii folosind generatorul de cod jOOQ, pentru a ob\u021bine un set de clase care reprezint\u0103 tabelele dumneavoastr\u0103, iar apoi scrie\u021bi interog\u0103ri tipizate pentru aceste tabele:<\/p>\n<pre><code class=\"java\">\tfor (Record2 record : DSL.using(configuration)\n\/\/   ^^^^^^^^^^^^^^^^^^^^^^^ Informa\u021biile despre tipuri sunt deduse pe baza codului generat la care se face referire \u00een condi\u021bia SELECT de mai jos\n\n       .select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)\n\/\/           vvvvv ^^^^^^^^^^^^  ^^^^^^^^^^^^^^^ nume generate\n       .from(ACTOR)\n       .orderBy(1, 2)) {\n    \/\/ ...\n}<\/code><\/pre>\n<p>\nCodul este generat fie manual \u00een afara compil\u0103rii, fie manual la fiecare compilare. De exemplu, o astfel de regenerare poate avea loc imediat dup\u0103 <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2014\/06\/25\/flyway-and-jooq-for-unbeatable-sql-development-productivity\/\">migr\u0103rile bazei de date Flyway, care pot fi, de asemenea, efectuate manual sau automat<\/a><\/noindex>.<\/p>\n<h4>Generarea de cod surs\u0103<\/h4>\n<p>\nCu astfel de abord\u0103ri \u00een generarea codului \u2013 manuale \u0219i automate \u2013 sunt asociate diverse filozofii, avantaje \u0219i dezavantaje pe care nu inten\u021bionez s\u0103 le discut detaliat \u00een aceast\u0103 articol. Dar, \u00een general, esen\u021ba codului generat este c\u0103 permite reproducerea pe Java a \u201eadev\u0103rului\u201d pe care \u00eel accept\u0103m ca un dat, fie \u00een cadrul sistemului nostru, fie \u00een afara acestuia. \u00centr-un anumit sens, acela\u0219i lucru este realizat de compilatoare, care genereaz\u0103 bytecode, cod ma\u0219in\u0103 sau alt tip de cod pe baza surselor \u2013 ob\u021binem o reprezentare a \u201eadev\u0103rului\u201d nostru \u00eentr-o alt\u0103 limb\u0103, indiferent de motivele specifice. <\/p>\n<p>Exist\u0103 numeroase astfel de generatoare de cod. De exemplu, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/technotes\/tools\/unix\/xjc.html\">XJC poate genera cod Java pe baza fi\u0219ierelor XSD sau WSDL.<\/a><\/noindex>Principiul este \u00eentotdeauna acela\u0219i:<\/p>\n<ul>\n<li>Exist\u0103 o anumit\u0103 adev\u0103r (intern sau extern) \u2013 de exemplu, specifica\u021bia, modelul de date etc.<\/li>\n<li>Avem nevoie de o reprezentare local\u0103 a acestui adev\u0103r \u00een limbajul nostru de programare.<\/li>\n<\/ul>\n<p>\n\u0218i, \u00een general, este \u00eentotdeauna rezonabil s\u0103 gener\u0103m o astfel de reprezentare \u2013 pentru a evita redundan\u021ba.<\/p>\n<h4>Furnizori de tipuri \u0219i procesarea anot\u0103rilor<\/h4>\n<p>\nDe remarcat: o alt\u0103 abordare mai modern\u0103 \u0219i specific\u0103 pentru generarea codului pentru jOOQ este asociat\u0103 cu utilizarea furnizorilor de tipuri, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/fsharp\/tutorials\/type-providers\/\">\u00een a\u0219a fel cum sunt implementa\u021bi \u00een F#.<\/a><\/noindex>\u00cen acest caz, codul este generat de compilator, efectiv \u00een etapa de compilare. \u00cen form\u0103 de surs\u0103, acest cod nu exist\u0103 \u00een principiu. \u00cen Java exist\u0103 instrumente similare, de\u0219i nu at\u00e2t de elegante \u2013 acestea sunt procesoarele de anotare, de exemplu, <noindex><a rel=\"nofollow\" href=\"https:\/\/projectlombok.org\">Lombok.<\/a><\/noindex>.<\/p>\n<p>\u00centr-un anumit sens, aici au loc acelea\u0219i lucruri ca \u00een prima situa\u021bie, cu excep\u021bia:<\/p>\n<ul>\n<li>Nu vede\u021bi codul generat (poate c\u0103 aceast\u0103 situa\u021bie nu pare at\u00e2t de resping\u0103toare pentru cineva?)<\/li>\n<li>Trebuie s\u0103 garanta\u021bi c\u0103 tipurile pot fi furnizate, adic\u0103 \u201eadev\u0103rul\u201d trebuie s\u0103 fie \u00eentotdeauna accesibil. Acest lucru este u\u0219or \u00een cazul Lombok, care anoteaz\u0103 \u201eadev\u0103rul\u201d. Este pu\u021bin mai complicat cu modelele de baze de date, a c\u0103ror func\u021bionare depinde de o conexiune activ\u0103 \u0219i constant disponibil\u0103.<\/li>\n<\/ul>\n<p><\/p>\n<h4>Care este problema cu generarea codului?<\/h4>\n<p>\nPe l\u00e2ng\u0103 \u00eentrebarea complex\u0103 despre cum este mai bine s\u0103 porne\u0219ti generarea de cod - manual sau automat, trebuie s\u0103 men\u021bion\u0103m \u0219i faptul c\u0103 exist\u0103 oameni care consider\u0103 c\u0103 generarea de cod este complet inutil\u0103. Justificarea acestui punct de vedere, pe care l-am \u00eent\u00e2lnit cel mai des, este c\u0103 este dificil s\u0103 configurezi o linie de asamblare. Da, cu adev\u0103rat este complicat. Apar costuri de infrastructur\u0103 suplimentare. Dac\u0103 abia \u00eencepi s\u0103 lucrezi cu un anumit produs (fie c\u0103 este vorba de jOOQ, JAXB, Hibernate, etc.), timpul necesar pentru a configura mediul de lucru este timp pe care ai dori s\u0103-l dedici studiului API-ului pentru a extrage valoare din acesta.<\/p>\n<p>Dac\u0103 costurile asociate cu \u00een\u021belegerea func\u021bion\u0103rii generatorului sunt prea mari, atunci, \u00eentr-adev\u0103r, API-ul a avut o utilizabilitate slab\u0103 a generatorului de cod (iar mai t\u00e2rziu se dovede\u0219te c\u0103 \u0219i personalizarea utilizatorului este complicat\u0103). U\u0219urin\u021ba de utilizare ar trebui s\u0103 fie o prioritate maxim\u0103 pentru orice astfel de API. Dar aceasta este doar un argument \u00eempotriva gener\u0103rii de cod. \u00cen rest, este complet necesar s\u0103 scrii manual o reprezentare local\u0103 a unei realit\u0103\u021bi interne sau externe.<\/p>\n<p>Mul\u021bi vor spune c\u0103 nu au timp s\u0103 se ocupe de toate acestea. Au termene limit\u0103 str\u00e2nse pentru Super-Produsul lor. Odat\u0103, ulterior, ne vom ocupa de liniile de asamblare, mai avem timp. Le voi r\u0103spunde:<\/p>\n<p><img decoding=\"async\" alt=\"Adev\u0103rul \u00eenainte de toate, sau de ce trebuie s\u0103 proiect\u0103m sistemul pe baza dispozitivului bazei de date\" 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'Rourk, Audience Stack<\/a><\/noindex> <\/p>\n<p><b>Dar \u00een Hibernate \/ JPA este at\u00e2t de simplu s\u0103 scrii cod \"pentru Java\". <\/b><\/p>\n<p>A\u0219a este. Pentru Hibernate \u0219i utilizatorii s\u0103i, aceasta este simultan o binecuv\u00e2ntare \u0219i un blestem. \u00cen Hibernate po\u021bi reda cu u\u0219urin\u021b\u0103 c\u00e2teva entit\u0103\u021bi, astfel:<\/p>\n<pre><code class=\"java\">\t@Entity\nclass Book {\n  @Id\n  int id;\n  String title;\n}<\/code><\/pre>\n<p>\n\u0218i aproape totul este gata. Acum, responsabilitatea Hibernate este de a genera detaliile \u201ecomplexe\u201d despre cum exact aceast\u0103 entitate va fi definit\u0103 \u00een DDL pentru dialectul t\u0103u 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 \u0219i \u00eencepem s\u0103 rul\u0103m aplica\u021bia. O oportunitate cu adev\u0103rat grozav\u0103 pentru a \u00eencepe rapid \u0219i a testa diverse lucruri.<\/p>\n<p>Cu toate acestea, permite\u021bi-mi. Am fost pu\u021bin mincinos.<\/p>\n<ul>\n<li>Dar va aplica Hibernate cu adev\u0103rat defini\u021bia acestei chei primare denumite?<\/li>\n<li>Va crea Hibernate un index \u00een TITLE? \u2013 \u0219tiu sigur c\u0103 ne va fi necesar. <\/li>\n<li>Va face Hibernate cu adev\u0103rat aceast\u0103 cheie identificatoare conform specifica\u021biei Identity?<\/li>\n<\/ul>\n<p>\nProbabil c\u0103 nu. Dac\u0103 dezvolta\u021bi proiectul vostru de la zero, este \u00eentotdeauna convenabil s\u0103 arunca\u021bi vechea baz\u0103 de date \u0219i s\u0103 genera\u021bi una nou\u0103, odat\u0103 ce a\u021bi ad\u0103ugat anot\u0103rile necesare. Astfel, entitatea Book va ar\u0103ta \u00een cele din urm\u0103 a\u0219a: <\/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>\nSuper. Genereaz\u0103 din nou. Din nou, \u00een acest caz, la \u00eenceput va fi foarte u\u0219or.<\/p>\n<h4>Dar mai t\u00e2rziu va trebui s\u0103 pl\u0103ti\u021bi pentru asta.<\/h4>\n<p>\nMai devreme sau mai t\u00e2rziu va trebui s\u0103 ie\u0219i\u021bi \u00een produc\u021bie. Atunci modelul respectiv nu va mai func\u021biona. Deoarece: <\/p>\n<p>\u00cen produc\u021bie nu va mai fi posibil s\u0103 arunca\u021bi vechea baz\u0103 de date \u0219i s\u0103 \u00eencepe\u021bi totul de la zero. Baza dvs. de date va deveni mo\u0219tenit\u0103.<\/p>\n<p>De acum \u00eenainte, va trebui s\u0103 scrie\u021bi <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2014\/06\/25\/flyway-and-jooq-for-unbeatable-sql-development-productivity\/\">scripturi de migrare DDL, de exemplu, cu ajutorul Flyway.<\/a><\/noindex>. Ce se va \u00eent\u00e2mpla cu entit\u0103\u021bile voastre \u00een acest caz? Pute\u021bi fie s\u0103 le adapta\u021bi manual (\u0219i astfel s\u0103 v\u0103 dubla\u021bi volumul de munc\u0103), fie s\u0103 cere\u021bi Hibernate s\u0103 le genereze din nou pentru voi (c\u00e2t de mari sunt \u0219ansele ca ceea ce va fi generat astfel s\u0103 corespund\u0103 a\u0219tept\u0103rilor voastre?) \u00cen orice caz, ave\u021bi de pierdut.<\/p>\n<p>Astfel, imediat ce trece\u021bi \u00een produc\u021bie, ve\u021bi avea nevoie de patch-uri rapide. Iar acestea trebuie implementate \u00een produc\u021bie foarte rapid. Deoarece nu v-a\u021bi preg\u0103tit \u0219i nu a\u021bi organizat o livrare lin\u0103 a migra\u021biilor voastre pentru produc\u021bie, ve\u021bi face totul \u00een \u00eent\u00e2rziere. Apoi nu ve\u021bi mai avea timp s\u0103 face\u021bi totul corect. \u0218i ve\u021bi da vina pe Hibernate, deoarece \u00eentotdeauna e vinovat oricine, doar nu voi\u2026<\/p>\n<p>\u00cen schimb, de la bun \u00eenceput, totul putea fi f\u0103cut complet diferit. De exemplu, s\u0103 pui ro\u021bi circulare pe biciclet\u0103.<\/p>\n<h4>Mai \u00eent\u00e2i baza de date.<\/h4>\n<p>\nAdev\u0103rata \u201eadev\u0103rat\u0103\u201d \u00een schema bazei voastre de date \u0219i \u201esuveranitatea\u201d asupra acesteia se afl\u0103 \u00een interiorul bazei de date. Schema este definit\u0103 doar \u00een baza de date \u0219i nic\u0103ieri altundeva, iar fiecare client are o copie a acestei scheme, a\u0219a c\u0103 este complet logic s\u0103 impune\u021bi respectarea schemei \u0219i integritatea acesteia, f\u0103c\u00e2nd acest lucru direct \u00een baza de date \u2013 acolo unde este stocat\u0103 informa\u021bia. <br \/>\nEste o \u00een\u021belepciune veche, chiar b\u0103tut\u0103. Cheile primare \u0219i unice sunt bune. Cheile externe \u2013 sunt bune. Verificarea constr\u00e2ngerilor \u2013 este bun\u0103. <noindex><a rel=\"nofollow\" href=\"https:\/\/community.oracle.com\/ideas\/13028\">Afirm\u0103rile<\/a><\/noindex> \u2013 sunt bune. <\/p>\n<p>\u0218i aceasta nu este tot. De exemplu, folosind Oracle, probabil c\u0103 dori\u021bi s\u0103 specifica\u021bi:<\/p>\n<ul>\n<li>\u00cen ce spa\u021biu de tabele se afl\u0103 tabela dumneavoastr\u0103 <\/li>\n<li>Ce valoare are PCTFREE <\/li>\n<li>Care este dimensiunea cache-ului din secven\u021ba dumneavoastr\u0103 (dup\u0103 identificator)<\/li>\n<\/ul>\n<p>\nPoate c\u0103 toate acestea nu sunt importante \u00een sistemele mici, dar nu trebuie s\u0103 a\u0219tepta\u021bi trecerea \u00een domeniul \u00abbig data\u00bb \u2014 pute\u021bi \u00eencepe s\u0103 profita\u021bi de optimiz\u0103rile de stocare oferite de furnizor chiar mai devreme, cum ar fi cele men\u021bionate mai sus. Niciun ORM pe care l-am v\u0103zut (inclusiv jOOQ) nu ofer\u0103 acces la \u00eentregul set de op\u021biuni DDL pe care a\u021bi putea dori s\u0103 le utiliza\u021bi \u00een baza de date. ORM-urile ofer\u0103 unele instrumente care ajut\u0103 la scrierea DDL.<\/p>\n<p>Dar, p\u00e2n\u0103 la urm\u0103, o schem\u0103 bine proiectat\u0103 este scris\u0103 manual \u00een DDL. Orice DDL generat este doar o aproxima\u021bie a acesteia.<\/p>\n<h4>Ce zice\u021bi de modelul clientului?<\/h4>\n<p>\nA\u0219a cum s-a men\u021bionat mai sus, pe client ve\u021bi avea nevoie de o copie a schemei bazei de date, o reprezentare client. Este de la sine \u00een\u021beles c\u0103 aceast\u0103 reprezentare client trebuie s\u0103 fie sincronizat\u0103 cu modelul real. Cum este cel mai bine s\u0103 realiz\u0103m acest lucru? Prin intermediul unui generator de cod.<\/p>\n<p>Toate bazele de date ofer\u0103 metainforma\u021biile lor prin SQL. Iat\u0103 cum s\u0103 ob\u021bine\u021bi toate tabelele din baza dumneavoastr\u0103 de date \u00een diferite dialecte SQL:<\/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>\nAceste interog\u0103ri (sau similare, \u00een func\u021bie de dac\u0103 trebuie s\u0103 lua\u021bi \u00een considerare \u0219i vizualiz\u0103rile, viziuni materializate, func\u021bii cu valori de tabel) sunt, de asemenea, executate prin apelul <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> din JDBC, sau prin intermediul modulului meta jOOQ.<\/p>\n<p>Din rezultatele acestor interog\u0103ri, este relativ u\u0219or s\u0103 genera\u021bi orice reprezentare client a modelului bazei de date, indiferent de tehnologia utilizat\u0103 pe client.<\/p>\n<ul>\n<li>Dac\u0103 utiliza\u021bi JDBC sau Spring, pute\u021bi crea un set de constante de tip \u0219ir <\/li>\n<li>Dac\u0103 folosi\u021bi JPA, pute\u021bi genera singuri entit\u0103\u021bile <\/li>\n<li>Dac\u0103 folosi\u021bi jOOQ, pute\u021bi genera meta-modelul jOOQ<\/li>\n<\/ul>\n<p>\n\u00cen func\u021bie de volumul de func\u021bionalit\u0103\u021bi oferite de API-ul clientului dumneavoastr\u0103 (de exemplu, jOOQ sau JPA), meta-modelul generat poate fi cu adev\u0103rat bogat \u0219i complet. S\u0103 lu\u0103m, de exemplu, posibilitatea unirilor implicite, <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2018\/02\/20\/type-safe-implicit-join-through-path-navigation-in-jooq-3-11\/\">ap\u0103rut\u0103 \u00een jOOQ 3.11<\/a><\/noindex>, care se bazeaz\u0103 pe informa\u021biile meta generate despre rela\u021biile dintre cheile externe care ac\u021bioneaz\u0103 \u00eentre tabelele dumneavoastr\u0103.<\/p>\n<p>Acum, orice modificare a bazei de date va duce automat la actualizarea codului clientului. Imagina\u021bi-v\u0103, de exemplu:<\/p>\n<pre><code class=\"sql\">ALTER TABLE book RENAME COLUMN title TO book_title;<\/code><\/pre>\n<p>\nChiar a\u021bi vrea s\u0103 face\u021bi aceast\u0103 munc\u0103 de dou\u0103 ori? Nici vorb\u0103. Pur \u0219i simplu \u00eenregistr\u0103m DDL-ul, \u00eel rul\u0103m prin canalul dumneavoastr\u0103 de compilare \u0219i ob\u021binem entitatea actualizat\u0103:<\/p>\n<pre><code class=\"java\">@Entity\n@Table(name = \"book\", indexes = {\n \n  \/\/ V-a\u021bi g\u00e2ndit vreodat\u0103 la asta?\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>\nSau clasa jOOQ actualizat\u0103. Majoritatea modific\u0103rilor DDL se reflect\u0103 \u0219i \u00een semantica, nu doar \u00een sintax\u0103. De aceea, este adesea util s\u0103 consulta\u021bi codul compilat pentru a vedea ce cod va (sau poate fi) afectat de modific\u0103rile bazei dumneavoastr\u0103 de date.<\/p>\n<h4>Adev\u0103rul unic<\/h4>\n<p>\nIndiferent de tehnologia pe care o utiliza\u021bi, \u00eentotdeauna exist\u0103 un model care constituie sursa unic\u0103 a adev\u0103rului pentru o anumit\u0103 subsistem\u0103 \u2013 sau, cel pu\u021bin, ar trebui s\u0103 ne str\u0103duim s\u0103 facem astfel \u0219i s\u0103 evit\u0103m confuzia enterprise, \u00een care \u201eadev\u0103rul\u201d este simultan peste tot \u0219i nic\u0103ieri. Totul poate fi mult mai simplu. Dac\u0103 pur \u0219i simplu schimba\u021bi fi\u0219iere XML cu un alt sistem, folosi\u021bi doar XSD. Consulta\u021bi meta-modelul INFORMATION_SCHEMA din jOOQ \u00een format XML:<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 este u\u0219or de \u00een\u021beles<\/li>\n<li>XSD marcheaz\u0103 foarte bine con\u021binutul XML \u0219i permite validarea pe toate limbile clientului <\/li>\n<li>XSD este bine versiunezi \u0219i are o compatibilitate \u00eenapoi dezvoltat\u0103 <\/li>\n<li>XSD poate fi transformat \u00een cod Java cu ajutorul XJC<\/li>\n<\/ul>\n<p>\nUltimul punct este important. C\u00e2nd comunic\u0103m cu un sistem extern prin mesaje XML, vrem s\u0103 ne asigur\u0103m de validitatea mesajelor noastre. Acest lucru este foarte u\u0219or de realizat folosind JAXB, XJC \u0219i XSD. Ar fi o nebunie s\u0103 ne a\u0219tept\u0103m ca, abord\u00e2nd designul \"Java mai \u00eent\u00e2i\", unde facem mesajele noastre sub form\u0103 de obiecte Java, acestea s\u0103 poat\u0103 fi reprezentate \u00eentr-un mod clar \u00een XML \u0219i trimise pentru a fi consumate de un alt sistem. XML-ul generat astfel ar fi de foarte slab\u0103 calitate, ne-documentat \u0219i dificil de dezvoltat. Dac\u0103 ar exista un acord de nivel de serviciu (SLA) pentru o astfel de interfa\u021b\u0103, l-am strica imediat.<\/p>\n<p>Adev\u0103rul este c\u0103 exact asta se \u00eent\u00e2mpl\u0103 constant cu API-urile pe JSON, dar asta este o alt\u0103 poveste, data viitoare o s\u0103 mai vorbesc... <\/p>\n<h4>Baze de date: este acela\u0219i lucru<\/h4>\n<p>\nC\u00e2nd lucra\u021bi cu baze de date, \u00een\u021belege\u021bi c\u0103 toate sunt, \u00een principiu, asem\u0103n\u0103toare. O baz\u0103 de date de\u021bine datele sale \u0219i ar trebui s\u0103 conduc\u0103 schema. Orice modificare adus\u0103 schemei trebuie implementat\u0103 direct pe DDL, astfel \u00eenc\u00e2t s\u0103 se actualizeze sursa unic\u0103 a adev\u0103rului.<\/p>\n<p>C\u00e2nd actualizarea sursei a avut loc, to\u021bi clien\u021bii ar trebui s\u0103-\u0219i actualizeze \u0219i copiile modelului. Unii clien\u021bi pot fi scri\u0219i \u00een Java folosind jOOQ \u0219i Hibernate sau JDBC (sau toate la un loc). Al\u021bi clien\u021bi pot fi scri\u0219i \u00een Perl (le doresc mult noroc), iar al\u021bii \u2013 \u00een C#. Nu conteaz\u0103. Principalul model se afl\u0103 \u00een baza de date. Modelele generate prin ORM sunt de obicei de slab\u0103 calitate, pu\u021bin documentate \u0219i greu de dezvoltat.<\/p>\n<p>De aceea, nu face\u021bi gre\u0219eli. De la \u00eenceput, nu face\u021bi gre\u0219eli. Lucra\u021bi, baz\u00e2ndu-v\u0103 pe baza de date. Construie\u0219te un asemenea pipeline de desf\u0103\u0219urare care poate fi automatizat. Include generatoare de cod pentru a copia cu u\u0219urin\u021b\u0103 modelul bazei de date \u0219i a-l desc\u0103rca pe clien\u021bi. \u0218i \u00eenceta\u021bi s\u0103 v\u0103 face\u021bi griji \u00een leg\u0103tur\u0103 cu generatoarele de cod. Ele sunt bune. Cu acestea ve\u021bi deveni mai productiv. Trebuie doar s\u0103 investi\u021bi pu\u021bin timp la \u00eenceput pentru a le configura \u2013 \u0219i pe parcurs, ve\u021bi avea ani de productivitate crescut\u0103, din care se va \u021bese povestea proiectului vostru.<\/p>\n<p>Nu mul\u021bumi\u021bi \u00eenc\u0103, mai t\u00e2rziu.<\/p>\n<h4>Explica\u021bie<\/h4>\n<p>\nPentru claritate: Aceast\u0103 articol nu promoveaz\u0103 \u00een niciun caz ideea c\u0103 modelul bazei de date ar trebui s\u0103 defineasc\u0103 \u00eentreaga sistem\u0103 (adic\u0103, domeniul, logica de afaceri etc.). \u00cen acest articol, discut despre faptul c\u0103 codul clientului care interac\u021bioneaz\u0103 cu baza de date ar trebui s\u0103 func\u021bioneze av\u00e2nd ca baz\u0103 modelul bazei de date, astfel \u00eenc\u00e2t modelul s\u0103 nu fie redat ca un entitate de clas\u0103 \u00eent\u00e2i \u00een sine. Aceast\u0103 logic\u0103 se afl\u0103, de obicei, la nivelul accesului la date pe clientul dumneavoastr\u0103.<\/p>\n<p>\u00cen arhitecturile cu dou\u0103 niveluri, care \u00eenc\u0103 mai exist\u0103 \u00een unele locuri, un astfel de model de sistem poate fi singura variant\u0103 posibil\u0103. Totu\u0219i, \u00een majoritatea sistemelor, nivelul accesului la date mi se pare a fi o \u00absub-sistem\u0103\u00bb, care incapsuleaz\u0103 modelul bazei de date.<\/p>\n<h4>Excep\u021bii<\/h4>\n<p>\nDin orice regul\u0103 exist\u0103 excep\u021bii, \u0219i am men\u021bionat deja c\u0103 abordarea centrat\u0103 pe baza de date \u0219i generarea codului surs\u0103 poate fi uneori inadecvat\u0103. Iat\u0103 c\u00e2teva astfel de excep\u021bii (probabil c\u0103 mai exist\u0103 \u0219i altele):<\/p>\n<ul>\n<li>C\u00e2nd schema este necunoscut\u0103 \u0219i trebuie s\u0103 fie deschis\u0103. De exemplu, sunte\u021bi un furnizor de instrumente care ajut\u0103 utilizatorii s\u0103 se orienteze \u00een orice schem\u0103. Uff. Aici este nevoie de generarea de cod. Dar totu\u0219i \u2013 baza de date trebuie s\u0103 fie prioritar\u0103.<\/li>\n<li>C\u00e2nd schema trebuie generat\u0103 \u00een timp real pentru a rezolva o anumit\u0103 problem\u0103. Acest exemplu pare s\u0103 fie o variant\u0103 u\u0219or extravagant\u0103 a pattern-ului <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Entity%E2%80%93attribute%E2%80%93value_model\">entity attribute value<\/a><\/noindex>, deci, \u00een realitate, nu ave\u021bi o schem\u0103 clar definit\u0103. \u00cen acest caz, adesea nu se poate fi sigur c\u0103 un RDBMS va fi adecvat.<\/li>\n<\/ul>\n<p>\nExcep\u021biile sunt, prin natura lor, excep\u021bionale. \u00cen majoritatea cazurilor care implic\u0103 utilizarea unui RDBMS, schema este cunoscut\u0103 din timp, se afl\u0103 \u00een interiorul RDBMS \u0219i reprezint\u0103 singura surs\u0103 de \"adev\u0103r\", iar to\u021bi clien\u021bii sunt nevoi\u021bi s\u0103 ob\u021bin\u0103 copii derivate din aceasta. \u00cen ideal, ar trebui implicat un generator de cod.<br \/>\n<br \/>Sursa: <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.2.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\/ro\/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.2.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"ro_RO\" \/>\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\/ro\/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\udd47Adev\u0103rul este prioritar, sau de ce sistemul trebuie s\u0103 fie proiectat av\u00e2nd la baz\u0103 structura bazei de date | ProHoster","description":"Salut, Habr! Continu\u0103m s\u0103 explor\u0103m subiectul","canonical_url":"https:\/\/prohoster.info\/ro\/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":"ro_RO","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\/ro\/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\/ro\/wp-json\/wp\/v2\/posts\/79465","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/comments?post=79465"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts\/79465\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media\/79466"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media?parent=79465"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/categories?post=79465"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/tags?post=79465"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}