Алдымен ақиқат немесе жүйені дерекқор құрылғысы негізінде жобалау неліктен қажет

Эй Хабр!

Біз тақырыпты зерттеуді жалғастырамыз Java и көктемсоның ішінде дерекқор деңгейінде. Бүгін біз үлкен қосымшаларды құрастырған кезде Java коды емес, дерекқордың құрылымы неліктен шешуші мәнге ие болуы керек, мұның қалай орындалатыны және осы ережеден қандай ерекшеліктер бар екендігі туралы оқуды ұсынамыз.

Бұл өте кешіктірілген мақалада мен неліктен барлық жағдайларда дерлік қолданбадағы деректер үлгісі «Java мүмкіндіктерінен» (немесе сіз кез келген клиент тілінде) емес, «деректер базасынан» жасалуы керек деп ойлайтынымды түсіндіремін. жұмыс істейді). Екінші тәсілді таңдай отырып, жобаңыз өсе бастағанда, сіз ұзақ ауыртпалық пен азап жолына түсесіз.

Мақала негізінде жазылған бір сұрақ, Stack Overflow ішінде берілген.

Бөлімде Reddit бойынша қызықты талқылаулар /r/java и /r/бағдарламалау.

Код генерациялау

JOOQ-пен танысқаннан кейін jOOQ іске қосу үшін бастапқы кодты генерациялауға шындап сенетініне ренжіген пайдаланушылардың соншалықты шағын қабаты бар екеніне таң қалдым. Ешкім сізге jOOQ-ды өзіңізге сәйкес келетіндей пайдалануыңызға кедергі келтірмейді және ешкім кодты құруды пайдалануға мәжбүрлемейді. Бірақ әдепкі бойынша (нұсқаулықта сипатталғандай) jOOQ келесідей жұмыс істейді: сіз (бұрынғы) дерекқор схемасынан бастайсыз, кестелеріңізді көрсететін сыныптар жинағын алу үшін оны jOOQ код генераторымен кері инженериялаңыз, содан кейін типті жазыңыз- осы кестелерге қарсы қауіпсіз сұраулар:

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

Код құрастырудан тыс қолмен немесе әрбір құрастыруда қолмен жасалады. Мысалы, мұндай регенерация бірден кейін болуы мүмкін Flyway дерекқорын тасымалдау, ол қолмен немесе автоматты түрде де орындалуы мүмкін.

Бастапқы кодты құру

Кодты құрудың осы тәсілдеріне байланысты әртүрлі философиялар, артықшылықтар мен кемшіліктер бар - қолмен және автоматты - мен оларды осы мақалада егжей-тегжейлі талқыламаймын. Бірақ, тұтастай алғанда, жасалған кодтың мәні мынада: ол Java-да біздің жүйеде немесе одан тыс жерде біз әдеттегідей қабылдайтын «шындықты» жаңғыртуға мүмкіндік береді. Белгілі бір мағынада, байт кодты, машиналық кодты немесе бастапқы кодтан кодтың басқа түрін жасайтын компиляторлар дәл осылай жасайды - біз нақты себептерге қарамастан, басқа тілдегі «ақиқаттың» көрінісін аламыз.

Мұндай код генераторлары көп. Мысалы, XJC XSD немесе WSDL файлдарына негізделген Java кодын жасай алады. Бұл принцип әрқашан бірдей:

  • Кейбір шындық (ішкі немесе сыртқы) бар - мысалы, спецификация, деректер үлгісі және т.б.
  • Бізге бағдарламалау тілімізде осы шындықтың жергілікті көрінісі қажет.

Сонымен қатар, артықшылықты болдырмау үшін әрдайым дерлік осындай өкілдік құру ұсынылады.

Түр жеткізушілері және аннотацияларды өңдеу

Ескерту: jOOQ үшін кодты генерациялаудың басқа, неғұрлым заманауи және нақты тәсілі типті провайдерлерді пайдалануды қамтиды, өйткені олар F# ішінде жүзеге асырылады. Бұл жағдайда кодты компилятор құрастырады, шын мәнінде компиляция сатысында. Негізінде мұндай код бастапқы кодтар түрінде болмайды. Java тілінде талғампаз болмаса да, ұқсас құралдар бар - бұл аннотация процессорлары, мысалы, Lombok.

Белгілі бір мағынада бұл жерде бірінші жағдайдағыдай нәрселер орын алады, тек мыналардан басқа:

  • Сіз жасалған кодты көрмейсіз (мүмкін бұл жағдай біреуге соншалықты жағымсыз болып көрінуі мүмкін бе?)
  • Түрлердің қамтамасыз етілуін қамтамасыз ету керек, яғни «шын» әрқашан қолжетімді болуы керек. «Шындықты» түсіндіретін Ломбок жағдайында бұл оңай. Әрқашан қолжетімді тікелей қосылымға тәуелді дерекқор үлгілерімен бұл сәл қиынырақ.

Кодты генерациялауда қандай мәселе бар?

Кодты генерациялауды қолмен немесе автоматты түрде қалай бастаған дұрыс деген күрделі сұраққа қоса, кодты генерациялау мүлдем қажет емес деп санайтын адамдар бар екенін айта кету керек. Мен жиі кездестіретін бұл көзқарастың негіздемесі сол кезде құрылыс құбырын орнату қиынға соғады. Иә, бұл шынымен қиын. Қосымша инфрақұрылымдық шығындар бар. Егер сіз белгілі бір өніммен (jOOQ, JAXB немесе Hibernate, т.б. болсын) жаңадан басталып жатсаңыз, оның мәнін алу үшін API интерфейсін үйренуге жұмсағыңыз келетін жұмыс үстелін орнату үшін уақыт қажет. .

Егер генератордың құрылғысын түсінуге байланысты шығындар тым жоғары болса, онда API код генераторының ыңғайлылығы бойынша нашар жұмыс істеді (және болашақта оны теңшеу де қиын екені белгілі болды). Кез келген осындай API үшін ыңғайлылық ең жоғары басымдылық болуы керек. Бірақ бұл кодты құруға қарсы бір ғана дәлел. Әйтпесе, ішкі немесе сыртқы шындықтың жергілікті көрінісін толығымен қолмен жазыңыз.

Көбісі мұның бәрін жасауға уақыт жоқ деп айтады. Олар супер өнімінің соңғы мерзімінде. Бір күні біз құрастыру конвейерлерін тараймыз, үлгереміз. Мен оларға жауап беремін:

Алдымен ақиқат немесе жүйені дерекқор құрылғысы негізінде жобалау неліктен қажет
Түпнұсқа, Алан О'Рурк, аудитория жинағы

Бірақ Hibernate/JPA режимінде «Java тілінде» код жазу өте оңай.

Шынымен. Hibernate және оның пайдаланушылары үшін бұл жақсылық та, қарғыс та. Күту күйінде сіз жай ғана бірнеше нысанды жаза аласыз, мысалы:

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

Және барлығы дерлік дайын. Енді Күту режимінің көп бөлігі - бұл нысанның SQL «диалектісінің» DDL-де дәл қалай анықталатыны туралы күрделі «мәліметтерді» жасау:

	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);

... және қолданбаны іске қосу. Тез тұрып, жұмыс істеуге және әртүрлі нәрселерді сынап көруге арналған керемет мүмкіндік.

Дегенмен, рұқсат етіңіз. Мен өтірік айттым.

  • Күту режимі осы аталған бастапқы кілттің анықтамасын шынымен орындай ма?
  • Күту режимі TITLE бойынша индекс жасайды ма? Бізге керек екенін анық білемін.
  • Күту күйі бұл кілтті сәйкестік сипаттамасында сәйкестендіру кілтіне айналдыра ма?

Мүмкін емес. Егер сіз өз жобаңызды нөлден бастап дамытатын болсаңыз, қажет аннотацияларды қосқаннан кейін ескі дерекқорды алып тастау және жаңасын жасау әрқашан ыңғайлы. Осылайша, Кітап нысаны ақыр соңында келесі пішінді алады:

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

Керемет. Қайта жасау. Тағы да, бұл жағдайда бастапқыда өте оңай болады.

Бірақ оны кейінірек төлеуге тура келеді.

Ерте ме, кеш пе, өндіріске түсуге тура келеді. Сол кезде модель жұмысын тоқтатады. Себебі:

Өндірісте, қажет болған жағдайда, ескі дерекқордан бас тарту және бәрін нөлден бастау мүмкін болмайды. Сіздің дерекқорыңыз бұрынғыға айналады.

Бұдан былай және мәңгілікке жазуға тура келеді DDL тасымалдау сценарийлері, мысалы, Flyway арқылы. Бұл жағдайда сіздің субъектілеріңізбен не болады? Сіз оларды қолмен бейімдей аласыз (және жұмыс жүктемеңізді екі есе арттыра аласыз) немесе күту режиміне оларды сіз үшін қайта қалпына келтіре аласыз (күтулеріңізді қанағаттандыру үшін осы жолмен жасалғаны қаншалықты ықтимал?) Сіз екі жолмен де ұтылып қаласыз.

Осылайша, өндіріске көшкен бойда сізге ыстық патчтар қажет болады. Және оларды өндіріске өте жылдам әкелу керек. Өндіріс үшін тасымалдауларыңызды біркелкі өткізуді дайындап, ұйымдастырмағандықтан, сіз жабайы түрде түзетіп жатырсыз. Содан кейін бәрін дұрыс жасауға уақыт жоқ. Сіз Гибернатқа ұрысасыз, өйткені бұл әрқашан біреудің кінәсі, бірақ сіз емес ...

Оның орнына, әу бастан барлығын мүлде басқаша жасауға болатын еді. Мысалы, велосипедке дөңгелек дөңгелектерді қойыңыз.

Алдымен деректер базасы

Дерекқор схемасындағы нақты «ақиқат» және оның үстінен «егемендік» дерекқорда жатыр. Схема дерекқордың өзінде ғана анықталған және басқа еш жерде жоқ, және әрбір клиентте осы схеманың көшірмесі бар, сондықтан схеманы және оның тұтастығын сақтауды қамтамасыз ету, оны дерекқорда дұрыс орындау - бұл жерде ақпарат сақталады.
Бұл тіпті ескірген даналық. Негізгі және бірегей кілттер жақсы. Шетелдік кілттер жақсы. Шектеуді тексеру жақсы. Бекіту - Жақсы.

Және бұл бәрі емес. Мысалы, Oracle көмегімен сіз мыналарды көрсеткіңіз келуі мүмкін:

  • Сіздің үстеліңіз қандай кесте кеңістігінде орналасқан
  • Оның PCTFREE мәні қандай
  • Сіздің ретіңіздегі кэш өлшемі қандай (идентификатордың артында)

Мұның бәрі шағын жүйелерде маңызды болмауы мүмкін, бірақ «үлкен деректер» саласына көшкенше күтудің қажеті жоқ - сіз сатушы ұсынатын сақтауды оңтайландырудан пайда көре бастай аласыз, мысалы, жоғарыда айтылғандар. Мен көрген ORM файлдарының ешқайсысы (соның ішінде jOOQ) дерекқорда пайдаланғыңыз келуі мүмкін DDL опцияларының толық жинағына қатынасты қамтамасыз етпейді. ORMs DDL жазуға көмектесетін кейбір құралдарды ұсынады.

Бірақ күннің соңында жақсы жобаланған схема DDL-де қолмен жазылады. Кез келген жасалған DDL оның жуықтауы ғана.

Клиент үлгісі туралы не деуге болады?

Жоғарыда айтылғандай, клиентте дерекқор схемасының көшірмесі, клиент көрінісі қажет болады. Айта кету керек, бұл клиент көрінісі нақты үлгімен синхрондалған болуы керек. Бұған қол жеткізудің ең жақсы жолы қандай? Код генераторымен.

Барлық дерекқорлар өздерінің мета-ақпараттарын SQL арқылы береді. Міне, дерекқорыңыздан әртүрлі SQL диалектілеріндегі барлық кестелерді алу жолы:

	-- 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

Бұл сұраулар (немесе көріністерді, материалдандырылған көріністерді, кестелік мәнді функцияларды ескеру қажет пе, соған байланысты ұқсас сұраулар) да шақыру арқылы орындалады. DatabaseMetaData.getTables() JDBC сайтынан немесе jOOQ мета-модульін пайдалану арқылы.

Осындай сұраулардың нәтижелері бойынша клиентте қандай технологияны пайдалансаңыз да, дерекқор үлгісінің кез келген клиенттік көрінісін жасау салыстырмалы түрде оңай.

  • JDBC немесе Spring пайдалансаңыз, жол тұрақтыларының жиынын жасауға болады
  • JPA пайдалансаңыз, нысандарды өздері жасай аласыз
  • jOOQ пайдалансаңыз, jOOQ мета үлгісін жасай аласыз

Клиенттік API (мысалы, jOOQ немесе JPA) қаншалықты мүмкіндік ұсынатынына байланысты жасалған метамодель шынымен бай және толық болуы мүмкін. Мысалы, жасырын қосылу мүмкіндігін алайық, jOOQ 3.11-де енгізілген, ол кестелер арасындағы сыртқы кілт қатынастары туралы жасалған мета-ақпаратқа негізделген.

Енді дерекқордың кез келген қадамы клиент кодын автоматты түрде жаңартады. Мысалы, елестетіп көріңіз:

ALTER TABLE book RENAME COLUMN title TO book_title;

Сіз бұл жұмысты екі рет жасағыңыз келе ме? Ешбір жағдайда. Біз жай ғана DDL тапсырамыз, оны құрастыру құбыры арқылы іске қосамыз және жаңартылған нысанды аламыз:

@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;
}

Немесе жаңартылған jOOQ сыныбы. DDL өзгерістерінің көпшілігі тек синтаксиске ғана емес, семантикаға да әсер етеді. Сондықтан, құрастырылған кодта дерекқорды ұлғайту арқылы қандай кодқа әсер ететінін (немесе болуы мүмкін) көру ыңғайлы болуы мүмкін.

Жалғыз шындық

Қандай технологияны пайдалансаңыз да, әрқашан кейбір ішкі жүйе үшін шындықтың жалғыз көзі болып табылатын бір модель бар - немесе, кем дегенде, біз осыған ұмтылуымыз керек және «шындық» бірден барлық жерде және еш жерде болмайтын кәсіпорынның шатасуына жол бермеуіміз керек. Барлығы әлдеқайда оңай болуы мүмкін. Егер сіз жай ғана XML файлдарын басқа жүйемен алмастырып жатсаңыз, жай ғана XSD пайдаланыңыз. XML пішініндегі jOOQ INFORMATION_SCHEMA мета-моделін қараңыз:
https://www.jooq.org/xsd/jooq-meta-3.10.0.xsd

  • XSD жақсы түсініледі
  • XSD XML мазмұнын өте жақсы белгілейді және барлық клиент тілдерінде тексеруге мүмкіндік береді
  • XSD жақсы нұсқада және өте кері үйлесімді
  • XSD XJC көмегімен Java кодына аударылуы мүмкін

Соңғы нүкте маңызды. XML хабарламаларын пайдаланып сыртқы жүйемен байланысқанда, біз хабарларымыздың жарамды екеніне көз жеткізгіміз келеді. Бұған JAXB, XJC және XSD көмегімен қол жеткізу өте оңай. Хабарламаларымызды Java нысандары ретінде жасайтын Java-бірінші дизайн тәсілінде олар қандай да бір жолмен XML-ге түсінікті етіп көрсетіліп, басқа жүйеге тұтынуға жіберіледі деп ойлаудың өзі ақылсыздық болар еді. Осылайша жасалған XML сапасы өте нашар, құжатталмаған және өңдеу қиын болады. Егер мұндай интерфейсте қызмет көрсету сапасы (SLA) деңгейі туралы келісім болса, біз оны бірден бұзатын едік.

Шынымды айтсам, бұл JSON API-де үнемі болатын нәрсе, бірақ бұл басқа әңгіме, мен келесі жолы дауласамын ...

Деректер базалары: олар бірдей

Деректер базасымен жұмыс істей отырып, сіз олардың барлығы бірдей екенін түсінесіз. Дерекқор оның деректеріне ие және схеманы басқаруы керек. Ақиқаттың жалғыз көзі жаңартылуы үшін схемаға жасалған кез келген өзгертулер тікелей DDL ішінде орындалуы керек.

Бастапқы жаңарту орын алған кезде, барлық тұтынушылар үлгінің көшірмелерін де жаңартуы керек. Кейбір клиенттер Java тілінде jOOQ және Hibernate немесе JDBC (немесе екеуі де) арқылы жазылуы мүмкін. Басқа клиенттер Perl тілінде (оларға сәттілік тілейік), басқалары C# тілінде жазылуы мүмкін. Бұл маңызды емес. Негізгі үлгі дерекқорда. ORM арқылы жасалған үлгілер әдетте сапасыз, құжатталмаған және әзірлеу қиын.

Сондықтан қателеспеңіз. Басынан қателеспеңіз. Деректер базасынан жұмыс. Автоматтандыруға болатын орналастыру құбырын жасаңыз. Дерекқор үлгісін ыңғайлы көшіру және оны клиенттерге тастау үшін код генераторларын қосыңыз. Код генераторлары туралы алаңдамаңыз. Олар жақсы. Олардың көмегімен сіз өнімдірек боласыз. Сізге тек басынан бастап оларды орнатуға аз уақыт жұмсасаңыз болғаны, жобаңыздың тарихын құру үшін бірнеше жылдар бойы жақсартылған өнімділікке ие боласыз.

Маған әлі алғыс айтпаңыз, кейінірек.

Түсіндіру

Түсінікті болу үшін: Бұл мақала ешбір жағдайда бүкіл жүйені (мысалы, домен, бизнес логикасы және т.б.) дерекқор үлгісіне сәйкес келтіру үшін икемді болуы керек екенін қолдамайды. Бұл мақалада айтып отырғаным, дерекқормен әрекеттесетін клиент коды дерекқор үлгісін «бірінші сынып» күйінде қайта жасамауы үшін дерекқор үлгісі негізінде әрекет етуі керек. Мұндай логика әдетте клиенттегі деректерге қол жеткізу деңгейінде орналасады.

Кейбір жерлерде әлі күнге дейін сақталған екі деңгейлі архитектурада мұндай жүйе моделі жалғыз мүмкін болуы мүмкін. Дегенмен, көптеген жүйелерде деректерге қол жеткізу деңгейі маған дерекқор үлгісін инкапсуляциялайтын «ішкі жүйе» болып көрінеді.

Ерекшеліктер

Әрбір ережеде ерекшеліктер бар және мен алдымен дерекқор мен бастапқы кодты құру тәсілі кейде орынсыз болуы мүмкін екенін бұрын айттым. Міне, осындай бірнеше ерекшелік (басқалары болуы мүмкін):

  • Схема белгісіз болғанда және оны ашу қажет. Мысалы, пайдаланушыларға кез келген диаграмманы шарлауға көмектесетін құралды бересіз. Фу. Мұнда код генерациясы жоқ. Бірақ бәрібір - ең алдымен деректер базасы.
  • Қандай да бір мәселені шешу үшін тізбекті жылдам жасау қажет болғанда. Бұл мысал өрнектің сәл бұралған нұсқасы сияқты нысан төлсипатының мәні, яғни сізде нақты анықталған схема жоқ. Бұл жағдайда сіз RDBMS сізге сәйкес келетініне жиі сенімді бола алмайсыз.

Ерекшеліктер табиғаты бойынша ерекше. RDBMS қолданумен байланысты көптеген жағдайларда схема алдын ала белгілі, ол RDBMS ішінде болады және «ақиқаттың» жалғыз көзі болып табылады және барлық клиенттер одан алынған көшірмелерді алуы керек. Ең дұрысы, бұл код генераторын қамтуы керек.

Ақпарат көзі: www.habr.com

пікір қалдыру