{"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\/hr\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh","title":{"rendered":"Istina na prvom mjestu, ili za\u0161to sustav treba projektirati na temelju strukture baze podataka","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Bok, Habr!<\/p>\n<p>Nastavljamo istra\u017eivati temu <noindex><a rel=\"nofollow\" href=\"https:\/\/www.piter.com\/collection\/all\/product\/sovremennyy-yazyk-java-lyambda-vyrazheniya-potoki-i-funktsionalnoe-programmirovanie\">Java<\/a><\/noindex> i <noindex><a rel=\"nofollow\" href=\"https:\/\/storage.piter.com\/upload\/contents\/978544611587\/978544611587_X.pdf\">Spring<\/a><\/noindex>, uklju\u010duju\u0107i i na razini baza podataka. Danas predla\u017eemo da pro\u010ditate za\u0161to prilikom dizajniranja velikih aplikacija struktura baze podataka, a ne Java kod, treba imati odlu\u010duju\u0107u va\u017enost, kako to raditi i koje su iznimke od ovog pravila.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex> <br \/>\nU ovom prili\u010dno zaka\u0161njelom \u010dlanku obja\u0161njavam za\u0161to smatram da u prakti\u010dki svim slu\u010dajevima model podataka u aplikaciji treba biti projektiran \"iz baze podataka\", a ne \"iz mogu\u0107nosti Jave\" (ili drugog klijentskog jezika s kojim radite). Biraju\u0107i drugi pristup, upu\u0161tate se na dug put boli i patnje \u010dim va\u0161 projekt po\u010dne rasti.<\/p>\n<p>\u010clanak je napisan prema motivima <noindex><a rel=\"nofollow\" href=\"https:\/\/stackoverflow.com\/questions\/50706556\/kotlin-with-jooq-and-write-table-models-manually-without-code-generation\">jednog pitanja<\/a><\/noindex>, postavljenog na Stack Overflow.<\/p>\n<p>Zanimljive rasprave na reddit u odjeljcima <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> i <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>Generiranje koda<\/h4>\n<p>\nKako me iznenadilo \u0161to postoji tako mali sloj korisnika koji, upoznav\u0161i se s jOOQ-om, negoduju zbog \u010dinjenice da se pri radu s jOOQ-om ozbiljno oslanja na generiranje izvornog koda. Nitko vas ne sprje\u010dava da koristite jOOQ onako kako smatrate potrebnim i ne prisiljava vas na kori\u0161tenje generiranja koda. No prema zadanim postavkama (kako je opisano u priru\u010dniku) rad s jOOQ-om izgleda ovako: zapo\u010dinjete s (naslije\u0111enom) shemom baze podataka, izvr\u0161avate njeno obrnuto projektiranje pomo\u0107u jOOQ generatora koda kako biste dobili skup klasa koje predstavljaju va\u0161e tablice, a zatim pi\u0161ete tip-safe upite za te tablice:<\/p>\n<pre><code class=\"java\">\tfor (Record2 record : DSL.using(configuration)\n\/\/   ^^^^^^^^^^^^^^^^^^^^^^^ Informacije o tipovima zaklju\u010dene su na \n\/\/   osnovu generiranog koda na koji se poziva navedeno\n\/\/   dolje uvjet SELECT\n\n       .select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)\n\/\/           vvvvv ^^^^^^^^^^^^  ^^^^^^^^^^^^^^^ generirana imena\n       .from(ACTOR)\n       .orderBy(1, 2)) {\n    \/\/ ...\n}<\/code><\/pre>\n<p>\nKod se generira ili ru\u010dno izvan gradnje, ili ru\u010dno pri svakoj gradnji. Na primjer, takva regeneracija mo\u017ee uslijediti odmah nakon <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2014\/06\/25\/flyway-and-jooq-for-unbeatable-sql-development-productivity\/\">migracije baze podataka Flyway, koju tako\u0111er mo\u017eete izvr\u0161iti ru\u010dno ili automatski<\/a><\/noindex>.<\/p>\n<h4>Generiranje izvornog koda<\/h4>\n<p>\nPristupi generiranju koda, kako ru\u010dni tako i automatski, povezani su s razli\u010ditim filozofijama, prednostima i nedostacima koje ne namjeravam detaljno razra\u0111ivati u ovom \u010dlanku. No, u su\u0161tini, cijela poanta generiranog koda je u tome da omogu\u0107uje reprodukciju na Javi te 'istine' koju prihva\u0107amo kao \u010dinjenicu, bilo unutar na\u0161eg sustava, bilo izvan njega. Na neki na\u010din, to isto rade kompajleri, generiraju\u0107i bajt-kod, strojni kod ili neku drugu vrstu koda na temelju izvornih kodova \u2013 dobivamo predstavu na\u0161e 'istine' na drugom jeziku, neovisno o specifi\u010dnim razlozima. <\/p>\n<p>Postoji mnogo takvih generatora koda. Na primjer, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/technotes\/tools\/unix\/xjc.html\">XJC mo\u017ee generirati Java kod na temelju XSD ili WSDL datoteka.<\/a><\/noindex>Princip je uvijek isti:<\/p>\n<ul>\n<li>Postoji neka istina (unutarnja ili vanjska) \u2013 primjerice, specifikacija, model podataka itd.<\/li>\n<li>Trebamo lokalnu reprezentaciju te istine na na\u0161em programskom jeziku.<\/li>\n<\/ul>\n<p>\nOsim toga, generirati takvu reprezentaciju gotovo uvijek je smisleno \u2013 kako bi se izbjegla suvi\u0161nost.<\/p>\n<h4>Provo\u0111enje tipova i obrada anotacija<\/h4>\n<p>\nNapomena: jo\u0161 jedan, moderniji i specifi\u010dniji pristup generaciji koda za jOOQ povezan je s kori\u0161tenjem provoditelja tipova, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/fsharp\/tutorials\/type-providers\/\">u obliku u kojem su implementirani u F#.<\/a><\/noindex>U tom slu\u010daju kod generira kompajler, zapravo u fazi kompajliranja. U obliku izvornog koda takav kod u na\u010delu ne postoji. U Javi postoje sli\u010dni, iako manje elegantni alati \u2013 to su procesori anotacija, na primjer, <noindex><a rel=\"nofollow\" href=\"https:\/\/projectlombok.org\">Lombok.<\/a><\/noindex>.<\/p>\n<p>U odre\u0111enom smislu, ovdje se de\u0161ava iste stvari kao u prvom slu\u010daju, osim \u0161to:<\/p>\n<ul>\n<li>Ne vidite generirani kod (mo\u017eda se nekome \u010dini da ta situacija nije toliko odbijaju\u0107a?)<\/li>\n<li>Morate jam\u010diti da se tipovi mogu pru\u017eiti, odnosno, 'istina' uvijek mora biti dostupna. To je lako u slu\u010daju Lomboka, koji anotira 'istinu'. Malo je slo\u017eenije s modelima baza podataka \u010diji rad ovisi o stalno dostupnoj aktivnoj vezi.<\/li>\n<\/ul>\n<p><\/p>\n<h4>Koji je problem s generacijom koda?<\/h4>\n<p>\nOsim zagonetnog pitanja o tome kako bolje pokrenuti generaciju koda \u2013 ru\u010dno ili automatski, treba spomenuti i da postoje ljudi koji smatraju da generacija koda uop\u0107e nije potrebna. Obja\u0161njenje ovog stava, koje sam naj\u010de\u0161\u0107e susretao, jest da je tada te\u0161ko podesiti tijek izgradnje. Da, zaista je te\u0161ko. Pojavljuju se dodatni infrastrukturni tro\u0161kovi. Ako tek po\u010dinjete raditi s odre\u0111enim proizvodom (bilo da je to jOOQ, JAXB, Hibernate itd.), na pode\u0161avanje radnog okru\u017eenja tro\u0161i se vrijeme koje biste radije potro\u0161ili na u\u010denje samog API-a kako biste kasnije izvukli vrijednost iz njega.<\/p>\n<p>Ako su tro\u0161kovi povezani s razumijevanjem radnje generatora preveliki \u2013 tada je u API-ju lo\u0161e odra\u0111eno korisni\u010dko su\u010delje generatora koda (a ispostavilo se da je i korisni\u010dko pode\u0161avanje u njemu slo\u017eeno). Upotrebljivost treba biti najvi\u0161i prioritet za bilo koji takav API. No, to je samo jedan argument protiv generacije koda. Ina\u010de, potpuno ru\u010dno pisanje lokalnog predstavljanja unutarnje ili vanjske istine.<\/p>\n<p>Mnogi \u0107e re\u0107i da nemaju vremena time se baviti. Imaju rokove za isporuku svog Super-Proizvoda. Kasnije \u0107emo srediti tijekove izgradnje, imat \u0107emo vremena. Ja im ka\u017eem:<\/p>\n<p><img decoding=\"async\" alt=\"Istina na prvom mjestu, ili za\u0161to sustav treba projektirati na temelju strukture baze podataka\" 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\/\">Izvorni tekst<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"http:\/\/audiencestack.com\/static\/blog.html\">Alan O\u2019Rourke, Audience Stack<\/a><\/noindex> <\/p>\n<p><b>Ali u Hibernateu \/ JPA tako je lako pisati kod \"za Javu\". <\/b><\/p>\n<p>Zaista. Za Hibernate i njegove korisnike to je istovremeno blagoslov i prokletstvo. U Hibernateu mo\u017eete jednostavno napisati nekoliko entiteta, ovako:<\/p>\n<pre><code class=\"java\">\t@Entity\nclass Knjiga {\n  @Id\n  int id;\n  String naslov;\n}<\/code><\/pre>\n<p>\nI gotovo je sve spremno. Sada je zadatak Hibernatea \u2013 generirati slo\u017eene \"detalje\" o tome kako \u0107e ova entitet biti definirana na DDL va\u0161eg \"dijalekta\" SQL:<\/p>\n<pre><code class=\"sql\">\tCREATE TABLE knjiga (\n  id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,\n  naslov VARCHAR(50),\n \n  CONSTRAINT pk_knjiga PRIMARY KEY (id)\n);\n \nCREATE INDEX i_knjiga_naslov ON knjiga (naslov);<\/code><\/pre>\n<p>\n\u2026 i po\u010dinjemo pokretati aplikaciju. Zaista izvrsna mogu\u0107nost za brzo zapo\u010dinjanje rada i isprobavanje razli\u010ditih stvari.<\/p>\n<p>Me\u0111utim, dozvolite. Prevario sam.<\/p>\n<ul>\n<li>Ho\u0107e li Hibernate zaista primijeniti definiciju ovog imenovanog primarnog klju\u010da?<\/li>\n<li>Ho\u0107e li Hibernate stvoriti indeks u NASLOVU? \u2013 Sigurno znam da \u0107e nam biti potreban. <\/li>\n<li>Ho\u0107e li Hibernate sigurno u\u010diniti ovaj klju\u010d identifikacijskim u Specifikaciji identiteta?<\/li>\n<\/ul>\n<p>\nVjerojatno ne. Ako razvijate svoj projekt od nule, uvijek je zgodno jednostavno odbaciti staru bazu podataka i generirati novu, \u010dim dodate potrebne anotacije. Tako \u0107e entitet Book na kraju izgledati ovako: <\/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. Ponovno generirati. Ponovno, u tom slu\u010daju, na po\u010detku \u0107e biti vrlo lako.<\/p>\n<h4>Ali kasnije \u0107ete za to morati platiti.<\/h4>\n<p>\nPr sooner ili kasnije morat \u0107ete pre\u0107i u produkciju. Tada \u0107e takav model prestati raditi. Zato \u0161to: <\/p>\n<p>U produkciji vi\u0161e ne\u0107ete mo\u0107i odbaciti staru bazu podataka i po\u010deti iznova s \u010distog lista. Va\u0161a baza podataka postat \u0107e naslije\u0111ena.<\/p>\n<p>Od sada i zauvijek morat \u0107ete pisati <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2014\/06\/25\/flyway-and-jooq-for-unbeatable-sql-development-productivity\/\">migracijske skripte DDL, primjerice, pomo\u0107u Flyway.<\/a><\/noindex>. A \u0161to \u0107e se tada dogoditi s va\u0161im entitetima? Mo\u017eete ih ili prilagoditi ru\u010dno (i tako pove\u0107ati svoj radni opseg), ili narediti Hibernateu da ih ponovno generira za vas (kolike su \u0161anse da \u0107e generirani entiteti odgovarati va\u0161im o\u010dekivanjima?) U svakom slu\u010daju gubite.<\/p>\n<p>Tako da, \u010dim pre\u0111ete u produkciju, trebaju vam hitne ispravke. A one se moraju isporu\u010diti u produkciju vrlo brzo. Budu\u0107i da se niste pripremili i organizirali glatku proizvodnu liniju za va\u0161e migracije, sve \u0107ete ga patchevati. A onda ne\u0107ete sti\u0107i sve u\u010diniti ispravno. I krivite Hibernate, jer je uvijek kriv netko drugi osim vas\u2026<\/p>\n<p>Umjesto toga, od samog po\u010detka sve se moglo raditi sasvim druga\u010dije. Na primjer, staviti okrugle kota\u010de na bicikl.<\/p>\n<h4>Prvo baza podataka.<\/h4>\n<p>\nPrava \"istina\" u shemi va\u0161e baze podataka i \"suverenitet\" nad njom le\u017ei unutar baze podataka. Shema se odre\u0111uje samo u samoj bazi podataka i nigdje drugdje, i svaki od klijenata ima kopiju te sheme, stoga je potpuno razumno nametati po\u0161tivanje sheme i njene cjelovitosti to\u010dno u bazi podataka - tamo gdje se podaci i pohranjuju. <br \/>\nTo je stara, \u010dak i potro\u0161ena mudrost. Primarni i jedinstveni klju\u010devi - to je dobro. Vanjski klju\u010devi - dobro. Provjera ograni\u010denja - dobro. <noindex><a rel=\"nofollow\" href=\"https:\/\/community.oracle.com\/ideas\/13028\">Izjave<\/a><\/noindex> - dobro. <\/p>\n<p>I to nije sve. Na primjer, koriste\u0107i Oracle, vjerojatno \u0107ete htjeti navesti:<\/p>\n<ul>\n<li>U kojem tablespace-u se nalazi va\u0161a tablica <\/li>\n<li>Koja je njezina vrijednost PCTFREE <\/li>\n<li>Kolika je veli\u010dina cache-a u va\u0161oj sekvenci (za identifikatorom)<\/li>\n<\/ul>\n<p>\nMo\u017eda sve ovo nije bitno za male sustave, ali ne morate \u010dekati prelazak u podru\u010dje \"velikih podataka\" \u2014 mo\u017eete po\u010deti koristiti prednosti optimizacija pohrane podataka od strane dobavlja\u010da, kao \u0161to su spomenute. Nijedna ORM koju sam vidio (uklju\u010duju\u0107i jOOQ) ne pru\u017ea pristup cjelokupnom skupu DDL opcija koje mo\u017eda \u017eelite koristiti u svojoj bazi podataka. ORM nudi neke alate koji poma\u017eu u pisanju DDL-a.<\/p>\n<p>Ali, na kraju, dobro projektirana shema je ru\u010dno napisana u DDL-u. Svaki generirani DDL je samo njega aproksimacija.<\/p>\n<h4>\u0160to je s klijentskim modelom?<\/h4>\n<p>\nKao \u0161to je ranije spomenuto, na klijentu \u0107e vam trebati kopija sheme va\u0161e baze podataka, klijentska predstavnost. Suvi\u0161no je re\u0107i da ova klijentska predstavnost mora biti sinkronizirana s pravim modelom. Kako to najbolje posti\u0107i? Pomo\u0107u generatora koda.<\/p>\n<p>Sve baze podataka pru\u017eaju svoju metainformaciju putem SQL-a. Evo kako dobiti sve tablice iz va\u0161e baze podataka na razli\u010ditim SQL dijalektima:<\/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>\nOvi upiti (ili sli\u010dni, ovisno o tome uzimate li u obzir i prikaze, materializirane prikaze, funkcije s tabli\u010dnim vrijednostima) tako\u0111er se izvr\u0161avaju pomo\u0107u poziva <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> iz JDBC-a, ili pomo\u0107u meta-modula jOOQ.<\/p>\n<p>Iz rezultata tih upita relativno je lako generirati bilo koju klijentsku predstavnost modela va\u0161e baze podataka, bez obzira na to koja tehnologija se koristi na klijentu.<\/p>\n<ul>\n<li>Ako koristite JDBC ili Spring, mo\u017eete stvoriti skup string konstantama <\/li>\n<li>Ako koristite JPA, mo\u017eete generirati sami entitete <\/li>\n<li>Ako koristite jOOQ, mo\u017eete generirati jOOQ meta-model<\/li>\n<\/ul>\n<p>\nOvisno o tome kakav opseg mogu\u0107nosti nudi va\u0161e klijentsko API (npr. jOOQ ili JPA), generirani meta-model mo\u017ee biti stvarno bogat i sveobuhvatan. Uzmimo, na primjer, mogu\u0107nost neizravnih spajanja, <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2018\/02\/20\/type-safe-implicit-join-through-path-navigation-in-jooq-3-11\/\">koja se pojavila u jOOQ 3.11<\/a><\/noindex>, koja se oslanja na generirane metainformacije o odnosima vanjskih klju\u010deva koji djeluju izme\u0111u va\u0161ih tablica.<\/p>\n<p>Sada \u0107e svaka promjena u bazi podataka automatski dovesti do a\u017euriranja klijentskog koda. Zamislite, na primjer:<\/p>\n<pre><code class=\"sql\">ALTER TABLE book RENAME COLUMN title TO book_title;<\/code><\/pre>\n<p>\nDoista biste \u017eeljeli raditi taj posao dvaput? Ni u kojem slu\u010daju. Samo zabilje\u017eite DDL, pro\u0111ite ga kroz va\u0161 gradbeni sustav i dobit \u0107ete a\u017euriranu entitet:<\/p>\n<pre><code class=\"java\">@Entity\n@Table(name = \"book\", indexes = {\n \n  \/\/ Jeste li o tome razmi\u0161ljali?\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>\nIli a\u017eurirana klasa jOOQ. Ve\u0107ina promjena DDL tako\u0111er se odra\u017eava na semantici, a ne samo na sintaksi. Stoga je korisno pogledati u kompajlirani kod koji \u0107e (ili mo\u017ee biti) zahva\u0107en promjenom va\u0161e baze podataka.<\/p>\n<h4>Jedina istina<\/h4>\n<p>\nBez obzira na tehnologiju koju koristite, uvijek postoji jedan model koji predstavlja jedini izvor istine za neku podsustavu \u2013 ili, barem, trebali bismo te\u017eiti tome i izbjegavati takvu poduzetni\u010dku zbrku gdje je \"istina\" odmah svugdje i nigdje. Sve mo\u017ee biti mnogo jednostavnije. Ako samo razmjenjujete XML datoteke s nekom drugom sustavom, jednostavno koristite XSD. Pogledajte metamodel INFORMATION_SCHEMA iz jOOQ u XML formatu:<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 je dobro razumljiv<\/li>\n<li>XSD vrlo dobro ozna\u010dava sadr\u017eaj XML-a i omogu\u0107uje validaciju na svim klijentskim jezicima <\/li>\n<li>XSD se dobro verzionira i ima razvijenu unazadnu kompatibilnost <\/li>\n<li>XSD se mo\u017ee prevesti u Java kod pomo\u0107u XJC<\/li>\n<\/ul>\n<p>\nPosljednja to\u010dka je va\u017ena. Kada komuniciramo s vanjskim sustavima putem XML poruka, \u017eelimo biti sigurni u validnost na\u0161ih poruka. To je vrlo lako posti\u0107i pomo\u0107u JAXB, XJC i XSD. Bilo bi potpuno ludilo o\u010dekivati da \u0107e se u pristupu dizajnu \"najprije Java\", gdje stvaramo na\u0161e poruke kao Java objekte, one na neki na\u010din razumno mapirati na XML i poslati za kori\u0161tenje u drugom sustavu. XML generiran na taj na\u010din bio bi vrlo lo\u0161e kvalitete, nedovoljno dokumentiran i te\u0161ko bi se razvijao. Kada bi postojala SLA za takav su\u010delje, odmah bismo je pokvarili.<\/p>\n<p>Iskreno, upravo se to neprekidno doga\u0111a s API-jem na JSON, ali to je ve\u0107 druga pri\u010da, sljede\u0107i put \u0107u se ljutiti\u2026 <\/p>\n<h4>Baze podataka: to je isto<\/h4>\n<p>\nKada radite s bazama podataka, shva\u0107ate da su sve one, u na\u010delu, sli\u010dne. Baza posjeduje svoje podatke i treba upravljati shemom. Sve modifikacije u shemi trebaju se provoditi izravno na DDL-u kako bi se a\u017eurirao jedinstveni izvor istine.<\/p>\n<p>Kada se a\u017eurira izvor, svi klijenti tako\u0111er moraju a\u017eurirati svoje kopije modela. Neki klijenti mogu biti napisani na Javi koriste\u0107i jOOQ i Hibernate ili JDBC (ili sve zajedno). Drugi klijenti mogu biti napisani u Perlu (sretno im), tre\u0107i u C#. Nije va\u017eno. Glavni model se nalazi u bazi podataka. Modeli generirani pomo\u0107u ORM obi\u010dno su lo\u0161e kvalitete, slabo dokumentirani i te\u0161ki za razvoj.<\/p>\n<p>Zato nemojte praviti gre\u0161ke. Od samog po\u010detka ne pravite gre\u0161ke. Radite na osnovi baze podataka. Izgradite takav pipeline za implementaciju koji se mo\u017ee automatizirati. Uklju\u010dite generatore koda kako biste lako mogli kopirati model va\u0161e baze podataka i prenijeti ga na klijente. I prestanite brinuti o generatorima koda. Oni su dobri. S njima \u0107ete postati produktivniji. Trebate samo na po\u010detku potro\u0161iti malo vremena na njihovo pode\u0161avanje \u2013 i zatim vas o\u010dekuju godine pove\u0107ane produktivnosti, od kojih \u0107e se sastojati povijest va\u0161eg projekta.<\/p>\n<p>Za sada ne zahvaljujte, kasnije.<\/p>\n<h4>Obja\u0161njenje<\/h4>\n<p>\nZa jasno\u0107u: Ovaj \u010dlanak nikako ne promovira ideju da se cijeli sustav (tj. predmetno podru\u010dje, poslovna logika itd.) mora prilagoditi modelu va\u0161e baze podataka. U ovom \u010dlanku govorim o tome da klijentski kod koji komunicira s bazom podataka treba djelovati temeljem modela baze podataka, tako da se u njemu samom ne replicira model baze podataka kao \"prvi razred\". Takva logika obi\u010dno se nalazi na razini pristupa podacima na strani klijenta.<\/p>\n<p>U dvostranim arhitekturama, koje su jo\u0161 uvijek prisutne u nekim mjestima, takav model sustava mo\u017ee biti jedini mogu\u0107i. Me\u0111utim, u ve\u0107ini sustava razina pristupa podacima mi se \u010dini kao \"pod sustav\", koji enkapsulira model baze podataka.<\/p>\n<h4>Izuzeci<\/h4>\n<p>\nSvakoj pravilu postoje izuzeci, i ve\u0107 sam spomenuo da pristup s primarno\u0161\u0107u baze podataka i generiranjem izvornog koda ponekad mo\u017ee biti neprimjeren. Evo nekoliko takvih izuzetaka (vjerojatno postoje i drugi):<\/p>\n<ul>\n<li>Kada je shema nepoznata i treba je otkriti. Na primjer, vi ste davatelj alata koji poma\u017ee korisnicima da se orijentiraju u bilo kojoj shemi. Uf. Ovdje je potrebno generirati kod. No, opet \u2013 baza podataka prije svega.<\/li>\n<li>Kada shema treba biti generirana u hodu za rje\u0161avanje odre\u0111enog zadatka. Ovaj primjer izgleda kao malo pretjerana verzija obrasca <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Entity%E2%80%93attribute%E2%80%93value_model\">entity attribute value<\/a><\/noindex>, tj. stvarno nemate jasno definiranu shemu. U ovom slu\u010daju, \u010desto se ne mo\u017ee biti siguran ho\u0107e li vam odgovarati RDBMS.<\/li>\n<\/ul>\n<p>\nIzuzeci su po prirodi iznimni. U ve\u0107ini slu\u010dajeva povezanih s kori\u0161tenjem RDBMS-a, shema je unaprijed poznata, smje\u0161tena unutar RDBMS-a i predstavlja jedini izvor \"istine\", dok svi klijenti moraju imati kopije, izvedene iz nje. U idealnom slu\u010daju, pri tome bi trebalo koristiti generator koda.<br \/>\n<br \/>Izvor: <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\/hr\/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=\"hr_HR\" \/>\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\/hr\/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\udd47Istina prije svega, ili za\u0161to sustav treba dizajnirati s obzirom na strukturu baze podataka | ProHoster","description":"Pozdrav, Habr! Nastavljamo istra\u017eivati temu","canonical_url":"https:\/\/prohoster.info\/hr\/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":"hr_HR","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\/hr\/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\/hr\/wp-json\/wp\/v2\/posts\/79465","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/comments?post=79465"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/posts\/79465\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/media\/79466"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/media?parent=79465"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/categories?post=79465"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/tags?post=79465"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}