Y gwir yn gyntaf, neu pam mae angen dylunio'r system yn seiliedig ar strwythur y gronfa ddata

Hei Habr!

Rydym yn parhau i archwilio'r pwnc Java и Gwanwyngan gynnwys ar lefel cronfa ddata. Heddiw rydym yn cynnig darllen pam, wrth ddylunio cymwysiadau mawr, mai strwythur y gronfa ddata, ac nid y cod Java, a ddylai fod o bwysigrwydd pendant, sut y gwneir hyn, a beth yw'r eithriadau i'r rheol hon.

Yn yr erthygl eithaf hwyr hon, byddaf yn esbonio pam fy mod yn meddwl, ym mron pob achos, y dylai'r model data mewn cymhwysiad gael ei ddylunio "o'r gronfa ddata" yn hytrach nag "o alluoedd Java" (neu ba bynnag iaith cleient ydych chi gweithio gyda). Trwy ddewis yr ail ddull, byddwch yn mynd i lwybr hir o boen a dioddefaint unwaith y bydd eich prosiect yn dechrau tyfu.

Ysgrifennwyd yr erthygl yn seiliedig ar un cwestiwn, a roddir ar Stack Overflow.

Trafodaethau diddorol ar reddit mewn adrannau /r/ java и /r/ rhaglennu.

Cynhyrchu cod

Rwyf wedi fy synnu bod yna haen mor fach o ddefnyddwyr sydd, ar ôl dod yn gyfarwydd â jOOQ, yn digio'r ffaith bod jOOQ yn dibynnu'n ddifrifol ar gynhyrchu cod ffynhonnell i redeg. Nid oes unrhyw un yn eich atal rhag defnyddio jOOQ fel y gwelwch yn dda, ac nid oes neb yn eich gorfodi i ddefnyddio cynhyrchu cod. Ond yn ddiofyn (fel y disgrifir yn y llawlyfr), mae jOOQ yn gweithio fel hyn: rydych chi'n dechrau gyda sgema cronfa ddata (etifeddiaeth), ei beiriannu'n ôl gyda'r generadur cod jOOQ i gael set o ddosbarthiadau sy'n cynrychioli eich tablau, ac yna ysgrifennu teipiwch- ymholiadau diogel yn erbyn y tablau hyn:

	for (Record2<String, String> record : DSL.using(configuration)
//   ^^^^^^^^^^^^^^^^^^^^^^^ Информация о типах выведена на 
//   основании сгенерированного кода, на который ссылается приведенное
// ниже условие SELECT 
 
       .select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)
//           vvvvv ^^^^^^^^^^^^  ^^^^^^^^^^^^^^^ сгенерированные имена
       .from(ACTOR)
       .orderBy(1, 2)) {
    // ...
}

Cynhyrchir y cod naill ai â llaw y tu allan i'r adeilad, neu â llaw ar bob adeilad. Er enghraifft, gall adfywiad o'r fath ddilyn yn syth ar ôl hynny Mudo cronfa ddata Flyway, y gellir ei wneud â llaw neu'n awtomatig hefyd.

Cynhyrchu cod ffynhonnell

Mae yna amryw o athroniaethau, manteision ac anfanteision yn gysylltiedig â'r dulliau hyn o gynhyrchu cod - â llaw ac yn awtomatig - nad wyf yn mynd i'w trafod yn fanwl yn yr erthygl hon. Ond, yn gyffredinol, holl bwynt y cod a gynhyrchir yw ei fod yn caniatáu ichi atgynhyrchu yn Java y “gwirionedd” yr ydym yn ei gymryd yn ganiataol, naill ai o fewn ein system neu y tu allan iddi. Mewn ffordd, mae casglwyr sy'n cynhyrchu bytecode, cod peiriant, neu ryw fath arall o god o god ffynhonnell yn gwneud yr un peth - rydyn ni'n cael cynrychiolaeth o'n "gwirionedd" mewn iaith arall, waeth beth fo'r rhesymau penodol.

Mae yna lawer o gynhyrchwyr cod o'r fath. Er enghraifft, Gall XJC gynhyrchu cod Java yn seiliedig ar ffeiliau XSD neu WSDL. Mae'r egwyddor bob amser yr un fath:

  • Mae rhywfaint o wirionedd (mewnol neu allanol) - er enghraifft, manyleb, model data, ac ati.
  • Mae angen cynrychiolaeth leol o'r gwirionedd hwn yn ein hiaith raglennu.

Ar ben hynny, mae bron bob amser yn ddoeth cynhyrchu cynrychiolaeth o'r fath - er mwyn osgoi dileu swyddi.

Darparwyr Math a Phrosesu Anodi

Nodyn: Mae dull arall, mwy modern a phenodol o gynhyrchu cod ar gyfer jOOQ yn cynnwys defnyddio darparwyr math, wrth iddynt gael eu gweithredu yn F#. Yn yr achos hwn, mae'r cod yn cael ei gynhyrchu gan y casglwr, mewn gwirionedd ar y cam llunio. Mewn egwyddor, nid yw cod o'r fath yn bodoli ar ffurf codau ffynhonnell. Yn Java, mae offer tebyg, er nad mor gain, - proseswyr anodi yw'r rhain, er enghraifft, Lombok.

Mewn rhai ystyr, mae'r un pethau'n digwydd yma ag yn yr achos cyntaf, ac eithrio:

  • Nid ydych chi'n gweld y cod a gynhyrchir (efallai nad yw'r sefyllfa hon yn ymddangos mor wrthun i rywun?)
  • Rhaid i chi sicrhau y gellir darparu mathau, hynny yw, rhaid i "wir" fod ar gael bob amser. Mae hyn yn hawdd yn achos Lombok, sy'n anodi "gwirionedd". Mae ychydig yn anoddach gyda modelau cronfa ddata sy'n dibynnu ar gysylltiad byw sydd bob amser ar gael.

Beth yw'r broblem gyda chynhyrchu cod?

Yn ogystal â'r cwestiwn dyrys o sut mae'n well dechrau cynhyrchu cod - â llaw neu'n awtomatig, mae'n rhaid i mi sôn bod yna bobl sy'n credu nad oes angen cynhyrchu cod o gwbl. Y cyfiawnhad dros y safbwynt hwn, y deuthum ar ei draws amlaf, yw ei bod yn anodd wedyn sefydlu’r biblinell adeiladu. Ydy, mae'n anodd iawn. Mae costau seilwaith ychwanegol. Os ydych chi newydd ddechrau gyda chynnyrch penodol (boed yn jOOQ, neu JAXB, neu gaeafgysgu, ac ati), mae'n cymryd amser i sefydlu mainc waith yr hoffech chi ei dreulio yn dysgu'r API ei hun i gael gwerth ohono .

Os yw'r costau sy'n gysylltiedig â deall dyfais y generadur yn rhy uchel, yna, yn wir, gwnaeth yr API waith gwael ar ddefnyddioldeb y generadur cod (ac yn y dyfodol mae'n troi allan bod addasu ynddo hefyd yn anodd). Dylai defnyddioldeb fod yn flaenoriaeth uchaf ar gyfer unrhyw API o'r fath. Ond dim ond un ddadl yn erbyn cynhyrchu cod yw honno. Fel arall, ysgrifennwch yn gyfan gwbl â llaw y cynrychioliad lleol o wirionedd mewnol neu allanol.

Bydd llawer yn dweud nad oes ganddyn nhw amser i wneud hyn i gyd. Maen nhw ar y dyddiad cau ar gyfer eu Cynnyrch Gwych. Ryw ddiwrnod yn ddiweddarach byddwn yn cribo'r cludwyr cynulliad, bydd gennym amser. Byddaf yn eu hateb:

Y gwir yn gyntaf, neu pam mae angen dylunio'r system yn seiliedig ar strwythur y gronfa ddata
Gwreiddiol, Alan O'Rourke, Cynulleidfa Stack

Ond yn Hibernate / JPA mae mor hawdd ysgrifennu cod "yn Java".

Yn wir. I gaeafgysgu a'i ddefnyddwyr, mae hyn yn hwb ac yn felltith. Yn gaeafgysgu, gallwch chi ysgrifennu cwpl o endidau, fel hyn:

	@Entity
class Book {
  @Id
  int id;
  String title;
}

Ac mae bron popeth yn barod. Nawr mae llawer o Aeafgysgu i gynhyrchu "manylion" cymhleth o sut yn union y bydd yr endid hwn yn cael ei ddiffinio yn DDL eich "tafodiaith" yn SQL:

	CREATE TABLE book (
  id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
  title VARCHAR(50),
 
  CONSTRAINT pk_book PRIMARY KEY (id)
);
 
CREATE INDEX i_book_title ON book (title);

... a dechrau rhedeg y cais. Nodwedd cŵl iawn i godi a rhedeg yn gyflym a rhoi cynnig ar wahanol bethau.

Fodd bynnag, gadewch i mi. Roeddwn i'n dweud celwydd.

  • A fydd gaeafgysgu mewn gwirionedd yn gorfodi'r diffiniad o'r allwedd gynradd hon a enwir?
  • A fydd gaeafgysgu yn creu mynegai ar TITLE? Rwy'n gwybod yn sicr bod ei angen arnom.
  • A fydd gaeafgysgu yn gwneud yr allwedd hon yn allwedd hunaniaeth yn y Fanyleb Hunaniaeth?

Mae'n debyg na. Os ydych chi'n datblygu'ch prosiect o'r dechrau, mae bob amser yn gyfleus taflu'r hen gronfa ddata a chynhyrchu un newydd cyn gynted ag y byddwch chi'n ychwanegu'r anodiadau angenrheidiol. Felly, bydd yr endid Llyfr yn y pen draw ar y ffurf:

	@Entity
@Table(name = "book", indexes = {
  @Index(name = "i_book_title", columnList = "title")
})
class Book {
  @Id
  @GeneratedValue(strategy = IDENTITY)
  int id;
  String title;
}

Cwl. Adfywio. Unwaith eto, yn yr achos hwn, bydd yn hawdd iawn ar y dechrau.

Ond bydd yn rhaid i chi dalu amdano yn nes ymlaen.

Yn hwyr neu'n hwyrach bydd yn rhaid i chi ddechrau cynhyrchu. Dyna pryd mae'r model yn stopio gweithio. Achos:

Wrth gynhyrchu, ni fydd yn bosibl mwyach, os oes angen, i daflu'r hen gronfa ddata a dechrau popeth o'r dechrau. Bydd eich cronfa ddata yn troi'n un etifeddiaeth.

O hyn ymlaen ac am byth bydd yn rhaid i chi ysgrifennu Sgriptiau mudo DDL, e.e. defnyddio Flyway. A beth fydd yn digwydd i'ch endidau yn yr achos hwn? Gallwch naill ai eu teilwra â llaw (a dyblu eich llwyth gwaith) neu gael gaeafgysgu eu hadfywio i chi (pa mor debygol yw'r un a gynhyrchir fel hyn i gwrdd â'ch disgwyliadau?) Byddwch yn colli y naill ffordd neu'r llall.

Felly, cyn gynted ag y byddwch chi'n symud i mewn i gynhyrchu, bydd angen clytiau poeth arnoch chi. Ac mae angen dod â nhw i gynhyrchu yn gyflym iawn. Gan nad ydych wedi paratoi a threfnu piblinellau llyfn o'ch mudo ar gyfer cynhyrchu, rydych chi'n clytio'n wyllt. Ac yna nid oes gennych amser i wneud popeth yn iawn. Ac rydych chi'n casáu gaeafgysgu, oherwydd mae pawb ar fai bob amser, ond nid chi ...

Yn lle hynny, o'r cychwyn cyntaf, gallai popeth fod wedi'i wneud yn hollol wahanol. Er enghraifft, rhowch olwynion crwn ar feic.

Cronfa ddata yn gyntaf

Mae'r "gwir" go iawn yn eich sgema cronfa ddata a "sofraniaeth" drosto yn gorwedd o fewn y gronfa ddata. Diffinnir y sgema yn y gronfa ddata ei hun yn unig ac yn unman arall, ac mae gan bob un o'r cleientiaid gopi o'r sgema hwn, felly mae'n gwneud synnwyr perffaith i orfodi ymlyniad at y sgema a'i gyfanrwydd, i'w wneud yn iawn yn y gronfa ddata - lle mae'r gwybodaeth yn cael ei storio.
Mae hyn yn hen hyd yn oed hacneyed doethineb. Mae allweddi cynradd ac unigryw yn dda. Mae allweddi tramor yn iawn. Mae gwirio cyfyngiadau yn dda. Datganiadau - Iawn.

Ac, nid dyna'r cyfan. Er enghraifft, gan ddefnyddio Oracle, mae'n debyg y byddech am nodi:

  • Ym mha ofod bwrdd mae eich bwrdd
  • Beth yw ei gwerth PCTFREE
  • Beth yw maint y storfa yn eich dilyniant (tu ôl i'r id)

Efallai na fydd hyn i gyd o bwys mewn systemau bach, ond nid oes angen aros tan y newid i faes "data mawr" - gallwch ddechrau elwa o optimeiddio storio a ddarperir gan y gwerthwr, fel y rhai a grybwyllwyd uchod, yn llawer cynharach. Nid yw'r un o'r ORMs a welais (gan gynnwys jOOQ) yn darparu mynediad i'r set lawn o opsiynau DDL y gallech fod am eu defnyddio yn eich cronfa ddata. Mae ORMs yn cynnig rhai offer i'ch helpu i ysgrifennu DDL.

Ond ar ddiwedd y dydd, mae sgema wedi'i ddylunio'n dda yn cael ei ysgrifennu â llaw yn DDL. Dim ond brasamcan ohono yw unrhyw DDL a gynhyrchir.

Beth am y model cleient?

Fel y soniwyd uchod, ar y cleient bydd angen copi o'ch sgema cronfa ddata, barn y cleient. Afraid dweud, rhaid i farn y cleient hwn fod yn gyson â'r model go iawn. Beth yw'r ffordd orau o gyflawni hyn? Gyda generadur cod.

Mae pob cronfa ddata yn darparu eu meta-wybodaeth trwy SQL. Dyma sut i gael yr holl dablau mewn gwahanol dafodieithoedd SQL o'ch cronfa ddata:

	-- H2, HSQLDB, MySQL, PostgreSQL, SQL Server
SELECT table_schema, table_name
FROM information_schema.tables
 
-- DB2
SELECT tabschema, tabname
FROM syscat.tables
 
-- Oracle
SELECT owner, table_name
FROM all_tables
 
-- SQLite
SELECT name
FROM sqlite_master
 
-- Teradata
SELECT databasename, tablename
FROM dbc.tables

Mae'r ymholiadau hyn (neu rai tebyg, yn dibynnu a oes angen i chi hefyd ystyried safbwyntiau, safbwyntiau wedi'u gwireddu, swyddogaethau sy'n werth tabl) hefyd yn cael eu cyflawni trwy ffonio DatabaseMetaData.getTables() gan JDBC, neu ddefnyddio'r meta-fodiwl jOOQ.

O ganlyniadau ymholiadau o'r fath, mae'n gymharol hawdd cynhyrchu unrhyw gynrychiolaeth ochr y cleient o'ch model cronfa ddata, ni waeth pa dechnoleg a ddefnyddiwch ar y cleient.

  • Os ydych yn defnyddio JDBC neu Spring gallwch greu set o gysonion llinynnol
  • Os ydych chi'n defnyddio JPA, yna gallwch chi gynhyrchu'r endidau eu hunain
  • Os ydych chi'n defnyddio jOOQ gallwch chi gynhyrchu model meta jOOQ

Yn dibynnu ar faint o allu y mae eich API cleient yn ei gynnig (ee jOOQ neu JPA), gall y model meta a gynhyrchir fod yn gyfoethog ac yn gyflawn iawn. Cymerwch, er enghraifft, y posibilrwydd o uno ymhlyg, a gyflwynwyd yn jOOQ 3.11, sy'n dibynnu ar feta-wybodaeth a gynhyrchir am berthnasoedd allweddol tramor rhwng eich tablau.

Nawr bydd unrhyw gynyddydd cronfa ddata yn diweddaru cod y cleient yn awtomatig. Dychmygwch er enghraifft:

ALTER TABLE book RENAME COLUMN title TO book_title;

A fyddech chi wir yn hoffi gwneud y swydd hon ddwywaith? Mewn unrhyw achos. Rydyn ni'n ymrwymo'r DDL, yn ei redeg trwy'ch piblinell adeiladu, ac yn cael yr endid wedi'i ddiweddaru:

@Entity
@Table(name = "book", indexes = {
 
  // Вы об этом задумывались?
  @Index(name = "i_book_title", columnList = "book_title")
})
class Book {
  @Id
  @GeneratedValue(strategy = IDENTITY)
  int id;
 
  @Column("book_title")
  String bookTitle;
}

Neu'r dosbarth jOOQ wedi'i ddiweddaru. Mae'r rhan fwyaf o newidiadau DDL hefyd yn effeithio ar semanteg, nid cystrawen yn unig. Felly, gall fod yn gyfleus gweld yn y cod a luniwyd pa god y bydd (neu a allai) gael ei effeithio gan gynyddu eich cronfa ddata.

Yr unig wirionedd

Waeth pa dechnoleg rydych chi'n ei defnyddio, mae yna bob amser un model sy'n unig ffynhonnell gwirionedd ar gyfer rhai is-system - neu o leiaf dylem ymdrechu i hyn ac osgoi dryswch menter lle mae "gwirionedd" ym mhobman ac yn unman ar unwaith. Gall popeth fod yn llawer haws. Os ydych chi'n cyfnewid ffeiliau XML â rhyw system arall yn unig, defnyddiwch XSD. Edrychwch ar feta-fodel JOOQ INFORMATION_SCHEMA ar ffurf XML:
https://www.jooq.org/xsd/jooq-meta-3.10.0.xsd

  • Mae XSD yn cael ei ddeall yn dda
  • Mae XSD yn marcio cynnwys XML yn dda iawn ac yn caniatáu dilysu ym mhob iaith cleient
  • Mae XSD wedi'i fersiynau'n dda ac yn gydnaws iawn yn ôl
  • Gellir trosi XSD i god Java gan ddefnyddio XJC

Mae'r pwynt olaf yn bwysig. Wrth gyfathrebu â system allanol gan ddefnyddio negeseuon XML, rydym am fod yn siŵr bod ein negeseuon yn ddilys. Mae hyn yn hawdd iawn i'w gyflawni gyda JAXB, XJC a XSD. Gwallgofrwydd pur fyddai meddwl, mewn dull dylunio Java-gyntaf lle rydym yn gwneud ein negeseuon fel gwrthrychau Java, y gallent rywsut gael eu rendro'n ddealladwy i XML a'u hanfon i'w bwyta i system arall. Byddai'r XML a gynhyrchir yn y modd hwn o ansawdd gwael iawn, heb ei ddogfennu, ac yn anodd ei ddatblygu. Pe bai cytundeb ar lefel ansawdd gwasanaeth (SLA) ar ryngwyneb o'r fath, byddem yn ei sgriwio i fyny ar unwaith.

I fod yn onest, dyma'n union beth sy'n digwydd drwy'r amser gyda'r API JSON, ond stori arall yw honno, byddaf yn dadlau y tro nesaf ...

Cronfeydd data: maent yr un fath

Gan weithio gyda chronfeydd data, rydych chi'n deall eu bod i gyd yr un peth yn y bôn. Y gronfa ddata sy'n berchen ar ei data a rhaid iddi reoli'r sgema. Mae'n rhaid i unrhyw addasiadau a wneir i'r sgema gael eu gweithredu'n uniongyrchol yn DDL fel bod un ffynhonnell gwirionedd yn cael ei diweddaru.

Pan fydd y diweddariad ffynhonnell wedi digwydd, rhaid i bob cleient hefyd ddiweddaru eu copïau o'r model. Gall rhai cleientiaid gael eu hysgrifennu yn Java gan ddefnyddio jOOQ a Hibernate neu JDBC (neu'r ddau). Gall cleientiaid eraill gael eu hysgrifennu yn Perl (dymunwn lwc iddynt), eraill yn C#. Does dim ots. Mae'r prif fodel yn y gronfa ddata. Mae modelau a gynhyrchir gan ORM fel arfer o ansawdd gwael, wedi'u dogfennu'n wael, ac yn anodd eu datblygu.

Felly peidiwch â gwneud camgymeriadau. Peidiwch â gwneud camgymeriadau o'r cychwyn cyntaf. Gweithio o gronfa ddata. Adeiladu piblinell lleoli y gellir ei hawtomeiddio. Galluogi generaduron cod i gopïo'ch model cronfa ddata yn gyfleus a'i ollwng ar gleientiaid. A rhoi'r gorau i boeni am generaduron cod. Maen nhw'n dda. Gyda nhw, byddwch chi'n dod yn fwy cynhyrchiol. Y cyfan sydd angen i chi ei wneud yw treulio ychydig o amser yn eu gosod o'r cychwyn cyntaf, a bydd gennych flynyddoedd o berfformiad gwell i adeiladu stori eich prosiect.

Peidiwch â diolch i mi eto, yn ddiweddarach.

Eglurhad

I fod yn glir: Nid yw'r erthygl hon mewn unrhyw ffordd yn argymell bod angen ystwytho'r system gyfan (h.y., parth, rhesymeg busnes, ac ati, ac ati) i gyd-fynd â'ch model cronfa ddata. Yr hyn rwy'n siarad amdano yn yr erthygl hon yw y dylai cod cleient sy'n rhyngweithio â chronfa ddata weithredu ar sail model y gronfa ddata fel nad yw'n atgynhyrchu model y gronfa ddata mewn statws "dosbarth cyntaf". Mae rhesymeg o'r fath fel arfer wedi'i lleoli ar yr haen mynediad data ar eich cleient.

Mewn pensaernïaeth dwy lefel, sy'n dal i gael eu cadw mewn rhai mannau, efallai mai model system o'r fath yw'r unig un posibl. Fodd bynnag, yn y rhan fwyaf o systemau, mae'r haen mynediad data yn ymddangos i mi fel "is-system" sy'n crynhoi model y gronfa ddata.

Eithriadau

Mae eithriadau i bob rheol, ac rwyf wedi dweud o'r blaen y gall y gronfa ddata yn gyntaf a'r dull cynhyrchu cod ffynhonnell fod yn amhriodol weithiau. Dyma ychydig o eithriadau o'r fath (mae'n debyg bod eraill):

  • Pan nad yw'r sgema yn hysbys a bod angen ei agor. Er enghraifft, rydych chi'n darparu offeryn i helpu defnyddwyr i lywio unrhyw ddiagram. Phew. Nid oes unrhyw gynhyrchu cod yma. Ond yn dal i fod - y gronfa ddata yn gyntaf oll.
  • Pan fydd angen cynhyrchu cylched ar y hedfan i ddatrys rhyw broblem. Mae'n ymddangos bod yr enghraifft hon yn fersiwn ychydig yn frith o'r patrwm gwerth priodoledd endid, h.y., nid oes gennych sgema wedi'i ddiffinio'n dda mewn gwirionedd. Yn yr achos hwn, yn aml ni allwch hyd yn oed fod yn siŵr o gwbl y bydd RDBMS yn addas i chi.

Mae eithriadau yn ôl eu natur yn eithriadol. Yn y rhan fwyaf o achosion sy'n ymwneud â defnyddio RDBMS, mae'r sgema yn hysbys ymlaen llaw, mae y tu mewn i'r RDBMS a dyma'r unig ffynhonnell o "wirionedd", ac mae'n rhaid i bob cleient gaffael copïau sy'n deillio ohono. Yn ddelfrydol, dylai hyn gynnwys generadur cod.

Ffynhonnell: hab.com

Ychwanegu sylw