Avvalo haqiqat yoki nima uchun tizim ma'lumotlar bazasi dizayni asosida ishlab chiqilishi kerak

Hey Xabr!

Biz mavzuni o'rganishda davom etamiz Java и bahor, shu jumladan ma'lumotlar bazasi darajasida. Bugun biz sizni katta ilovalarni loyihalashda Java kodi emas, balki ma'lumotlar bazasi tuzilishi nima uchun hal qiluvchi ahamiyatga ega bo'lishi kerakligi, bu qanday amalga oshirilishi va ushbu qoidadan qanday istisnolar mavjudligi haqida o'qishni taklif qilamiz.

Ushbu juda kech maqolada men nima uchun deyarli barcha holatlarda ilovadagi ma'lumotlar modeli "Java imkoniyatlaridan" emas, balki "ma'lumotlar bazasidan" ishlab chiqilishi kerakligiga ishonishimni tushuntirib beraman (yoki siz qaysi mijoz tilida bo'lsangiz ham) bilan ishlash). Ikkinchi yondashuvni qo'llagan holda, loyihangiz rivojlana boshlagandan so'ng, siz o'zingizni uzoq og'riq va azob-uqubatlarga tayyorlayapsiz.

Maqola asosida yozilgan bitta savol, Stack Overflow da berilgan.

Bo'limlarda reddit bo'yicha qiziqarli muhokamalar /r/java и /r/dasturlash.

Kod yaratish

JOOQ bilan tanishib, jOOQ ishlash uchun manba kodini yaratishga jiddiy tayanishidan g'azablangan foydalanuvchilarning juda kichik qismi borligi meni hayratda qoldirdi. Hech kim sizni jOOQ-dan o'zingiz xohlagancha foydalanishingizga to'sqinlik qilmaydi yoki kod ishlab chiqarishdan foydalanishga majburlamaydi. Ammo jOOQ bilan ishlashning standart (qo'llanmada tasvirlanganidek) usuli shundan iboratki, siz (eski) ma'lumotlar bazasi sxemasidan boshlaysiz, uni jOOQ kod generatori yordamida teskari muhandislik bilan o'zgartirib, jadvallaringizni ifodalovchi sinflar to'plamini olasiz va keyin turini yozasiz. -bu jadvallar uchun xavfsiz so'rovlar:

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

Kod yig'ilishdan tashqarida qo'lda yoki har bir yig'ilishda qo'lda yaratiladi. Misol uchun, bunday regeneratsiya darhol keyin sodir bo'lishi mumkin Flyway ma'lumotlar bazasini ko'chirish, bu ham qo'lda yoki avtomatik ravishda amalga oshirilishi mumkin.

Manba kodini yaratish

Kod yaratishning ushbu yondashuvlari bilan bog'liq turli xil falsafalar, afzalliklar va kamchiliklar mavjud - qo'lda va avtomatik - men ushbu maqolada batafsil muhokama qilmoqchi emasman. Ammo, umuman olganda, yaratilgan kodning butun mohiyati shundaki, u bizning tizimimizda ham, undan tashqarida ham o'zimiz qabul qiladigan "haqiqatni" Java-da takrorlash imkonini beradi. Qaysidir ma'noda, kompilyatorlar bayt-kod, mashina kodi yoki manba kodining boshqa shaklini yaratganda shunday qiladi - biz aniq sabablardan qat'i nazar, boshqa tilda "haqiqatimiz" ifodasini olamiz.

Bunday kod generatorlari juda ko'p. Masalan, XJC XSD yoki WSDL fayllari asosida Java kodini yaratishi mumkin. Printsip har doim bir xil:

  • Haqiqat bor (ichki yoki tashqi) - masalan, spetsifikatsiya, ma'lumotlar modeli va boshqalar.
  • Bizning dasturlash tilimizda bu haqiqatning mahalliy ifodalanishi kerak.

Bundan tashqari, ortiqcha ishlamaslik uchun deyarli har doim bunday vakillikni yaratish tavsiya etiladi.

Turi provayderlari va izohlarni qayta ishlash

Eslatma: jOOQ uchun kod yaratishning yana bir, zamonaviyroq va o'ziga xos usuli - bu turdagi provayderlardan foydalanish, chunki ular F# da amalga oshiriladi. Bunday holda, kod kompilyator tomonidan, aslida kompilyatsiya bosqichida hosil bo'ladi. Aslida, bunday kod manba shaklida mavjud emas. Java-da shunga o'xshash vositalar mavjud, garchi unchalik nafis bo'lmasa ham, annotatsiya protsessorlari, masalan, Lombok.

Qaysidir ma'noda, bu erda xuddi birinchi holatda bo'lgani kabi sodir bo'ladi, bundan mustasno:

  • Siz yaratilgan kodni ko'rmayapsiz (ehtimol, bu holat kimgadir unchalik jirkanch ko'rinadi?)
  • Turlarning taqdim etilishiga ishonch hosil qilishingiz kerak, ya'ni "haqiqiy" har doim mavjud bo'lishi kerak. Bu "haqiqat" ni izohlaydigan Lombok misolida oson. Doimiy mavjud bo'lgan jonli ulanishga bog'liq bo'lgan ma'lumotlar bazasi modellari bilan bu biroz murakkabroq.

Kod yaratishda qanday muammo bor?

Kod ishlab chiqarishni qanday qilib eng yaxshi tarzda - qo'lda yoki avtomatik tarzda ishga tushirish haqidagi murakkab savolga qo'shimcha ravishda, kod ishlab chiqarish umuman kerak emas deb hisoblaydigan odamlar borligini ham eslatib o'tishimiz kerak. Men tez-tez uchragan bu nuqtai nazarni oqlash shundan iboratki, keyinchalik qurilish quvurini o'rnatish qiyin. Ha, bu juda qiyin. Qo'shimcha infratuzilma xarajatlari paydo bo'ladi. Agar siz ma'lum bir mahsulot (jOOQ, JAXB yoki Hibernate va h.k. bo'ladimi) bilan endigina boshlayotgan bo'lsangiz, ishlab chiqarish muhitini o'rnatish vaqt talab etadi, shuning uchun siz APIni o'zi o'rganishni afzal ko'rasiz, shunda undan qiymat olishingiz mumkin. .

Agar generatorning tuzilishini tushunish bilan bog'liq xarajatlar juda yuqori bo'lsa, unda API haqiqatan ham kod generatoridan foydalanish bo'yicha yomon ish qilgan (va keyinchalik ma'lum bo'lishicha, unda foydalanuvchini sozlash ham murakkab). Bunday API uchun qulaylik eng yuqori ustuvor bo'lishi kerak. Ammo bu kod ishlab chiqarishga qarshi faqat bitta dalil. Aks holda, ichki yoki tashqi haqiqatning mahalliy tasvirini yozish mutlaqo qo'lda.

Ko'pchilik bularning barchasini qilishga vaqtlari yo'qligini aytishadi. Ularning Super Mahsulotlari uchun muddat tugayapti. Qachondir yig‘uvchi konveyerlarni tartibga keltiramiz, vaqtimiz bo‘ladi. Men ularga javob beraman:

Avvalo haqiqat yoki nima uchun tizim ma'lumotlar bazasi dizayni asosida ishlab chiqilishi kerak
Original, Alan O'Rurk, tomoshabinlar to'plami

Lekin Hibernate/JPA da Java kodini yozish juda oson.

Haqiqatan ham. Hibernate va uning foydalanuvchilari uchun bu ham baraka, ham la'natdir. Hibernate rejimida siz shunchaki bir nechta ob'ektlarni yozishingiz mumkin, masalan:

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

Va deyarli hamma narsa tayyor. Endi bu ob'ekt SQL "dialektingiz" DDL-da aniq qanday aniqlanishi haqida murakkab "tafsilotlar"ni yaratish uchun Kutish rejimiga qoldiriladi:

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

... va ilovani ishga tushirishni boshlang. Tezda boshlash va turli narsalarni sinab ko'rish uchun ajoyib imkoniyat.

Biroq, menga ruxsat bering. Men yolg'on gapirdim.

  • Hibernate aslida ushbu asosiy kalitning ta'rifini amalga oshiradimi?
  • Kutish rejimi TITLE da indeks yaratadimi? - Bu bizga kerak bo'lishini aniq bilaman.
  • Hibernate bu kalitni identifikatsiya spetsifikatsiyasida aniq belgilaydimi?

Balki yo'q. Agar siz loyihangizni noldan ishlab chiqayotgan bo'lsangiz, kerakli izohlarni qo'shishingiz bilanoq eski ma'lumotlar bazasini bekor qilish va yangisini yaratish har doim qulaydir. Shunday qilib, Kitob ob'ekti oxir-oqibat quyidagi shaklga ega bo'ladi:

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

Ajoyib. Qayta tiklash. Shunga qaramay, bu holda boshida juda oson bo'ladi.

Ammo keyinroq to'lashingiz kerak bo'ladi

Ertami-kechmi siz ishlab chiqarishga kirishingiz kerak bo'ladi. O'shanda bu model ishlashni to'xtatadi. Chunki:

Ishlab chiqarishda, agar kerak bo'lsa, eski ma'lumotlar bazasidan voz kechish va noldan boshlash mumkin bo'lmaydi. Sizning ma'lumotlar bazangiz meros bo'lib qoladi.

Bundan buyon va abadiy yozishingiz kerak bo'ladi DDL migratsiya skriptlari, masalan, Flyway yordamida. Bunday holda sizning tashkilotlaringiz bilan nima sodir bo'ladi? Siz ularni qoʻlda moslashtirishingiz mumkin (va shu tariqa ish yukingizni ikki baravar oshirasiz) yoki Hibernate-ga ularni siz uchun qayta tiklashini aytishingiz mumkin (bu tarzda yaratilganlar sizning taxminlaringizni qondirish uchun qanchalik ehtimol?) Qanday boʻlmasin, yutqazasiz.

Shunday qilib, ishlab chiqarishga kirganingizdan so'ng sizga issiq yamalar kerak bo'ladi. Va ularni juda tez ishlab chiqarishga kiritish kerak. Siz ishlab chiqarish uchun migratsiyangizning silliq quvurini tayyorlamaganligingiz va tashkil qilmaganingiz uchun siz hamma narsani vahshiylik bilan tuzatasiz. Va keyin sizda hamma narsani to'g'ri bajarishga vaqtingiz yo'q. Va siz Hibernateni tanqid qilasiz, chunki bu har doim birovning aybi, faqat siz emas ...

Buning o'rniga, hamma narsa boshidan butunlay boshqacha bo'lishi mumkin edi. Masalan, dumaloq g'ildiraklarni velosipedga qo'ying.

Avval ma'lumotlar bazasi

Ma'lumotlar bazasi sxemasidagi haqiqiy "haqiqat" va uning ustidagi "suverenitet" ma'lumotlar bazasida yotadi. Sxema faqat ma'lumotlar bazasining o'zida aniqlanadi va boshqa hech qanday joyda yo'q va har bir mijozda ushbu sxemaning nusxasi mavjud, shuning uchun sxema va uning yaxlitligiga rioya qilishni ta'minlash, buni ma'lumotlar bazasida to'g'ri bajarish mantiqan to'g'ri keladi. saqlanadi.
Bu qadimiy, hatto o'ylab topilgan donolik. Asosiy va noyob kalitlar yaxshi. Chet el kalitlari yaxshi. Cheklovlarni tekshirish yaxshi. Bayonotlar - Yaxshi.

Bundan tashqari, bu hammasi emas. Masalan, Oracle-dan foydalanib, siz quyidagilarni belgilashni xohlaysiz:

  • Sizning stolingiz qaysi stol maydonida?
  • Uning PCTFREE qiymati qanday?
  • Sizning ketma-ketligingizdagi kesh hajmi qanday (id orqasida)

Bu kichik tizimlarda muhim boʻlmasligi mumkin, lekin siz katta maʼlumotlar sohasiga oʻtguningizcha kutishingiz shart emas — yuqorida aytib oʻtilganlar kabi sotuvchi tomonidan taqdim etilgan saqlash optimallashtirishlaridan tezroq foydalanishni boshlashingiz mumkin. Men ko'rgan ORMlarning hech biri (jOOQ ham) ma'lumotlar bazasida foydalanmoqchi bo'lgan DDL opsiyalarining to'liq to'plamiga kirishni ta'minlamaydi. ORMlar DDL yozishda yordam beradigan ba'zi vositalarni taklif qiladi.

Biroq, kunning oxirida, DDLda yaxshi ishlab chiqilgan sxema qo'lda yozilgan. Har qanday yaratilgan DDL faqat uning taxminiy ko'rinishidir.

Mijoz modeli haqida nima deyish mumkin?

Yuqorida aytib o'tilganidek, mijozda sizga ma'lumotlar bazasi sxemasining nusxasi, mijoz ko'rinishi kerak bo'ladi. Shuni ta'kidlash kerakki, ushbu mijoz ko'rinishi haqiqiy model bilan hamohang bo'lishi kerak. Bunga erishishning eng yaxshi yo'li qanday? Kod generatoridan foydalanish.

Barcha ma'lumotlar bazalari o'zlarining meta-ma'lumotlarini SQL orqali taqdim etadilar. Turli SQL dialektlarida maʼlumotlar bazasidan barcha jadvallarni qanday olish mumkin:

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

Ushbu so'rovlar (yoki shunga o'xshash so'rovlar, shuningdek, ko'rinishlarni, moddiylashtirilgan ko'rinishlarni, jadval qiymatli funktsiyalarni hisobga olishingiz kerakmi yoki yo'qligiga qarab) chaqiruv orqali ham bajariladi. DatabaseMetaData.getTables() JDBC dan yoki jOOQ meta-modulidan foydalanish.

Bunday so'rovlar natijalariga ko'ra, mijozda qaysi texnologiyadan foydalanmasligingizdan qat'i nazar, ma'lumotlar bazasi modelingizning mijoz tomonidagi har qanday ko'rinishini yaratish nisbatan oson.

  • Agar siz JDBC yoki Spring dan foydalanayotgan bo'lsangiz, satr konstantalari to'plamini yaratishingiz mumkin
  • Agar siz JPA dan foydalansangiz, ob'ektlarni o'zingiz yaratishingiz mumkin
  • Agar siz jOOQ dan foydalansangiz, jOOQ meta-modelini yaratishingiz mumkin

Mijoz API (masalan, jOOQ yoki JPA) tomonidan qancha funksionallik taklif qilinganiga qarab, yaratilgan metamodel haqiqatan ham boy va toʻliq boʻlishi mumkin. Masalan, yashirin qo'shilish imkoniyatini olaylik, jOOQ 3.11 da kiritilgan, bu sizning jadvallaringiz o'rtasida mavjud bo'lgan tashqi kalit aloqalari haqida yaratilgan meta-ma'lumotlarga tayanadi.

Endi har qanday ma'lumotlar bazasi o'sishi mijoz kodini avtomatik ravishda yangilaydi. Masalan, tasavvur qiling:

ALTER TABLE book RENAME COLUMN title TO book_title;

Haqiqatan ham bu ishni ikki marta qilishni xohlaysizmi? Hech qanday holatda. Shunchaki DDL-ni topshiring, uni qurish quvuri orqali boshqaring va yangilangan ob'ektni oling:

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

Yoki yangilangan jOOQ klassi. Ko'pgina DDL o'zgarishlari nafaqat sintaksisga, balki semantikaga ham ta'sir qiladi. Shuning uchun, ma'lumotlar bazasi o'sishi qaysi kodga ta'sir qilishini (yoki) ko'rish uchun tuzilgan kodni ko'rib chiqish foydali bo'lishi mumkin.

Yagona haqiqat

Qaysi texnologiyadan foydalanmasligingizdan qat'i nazar, har doim biron bir quyi tizim uchun haqiqatning yagona manbai bo'lgan bitta model mavjud - yoki, hech bo'lmaganda, biz bunga intilishimiz va "haqiqat" hamma joyda va bir vaqtning o'zida hech qayerda bo'lmagan korxona chalkashliklaridan qochishimiz kerak. . Hamma narsa ancha sodda bo'lishi mumkin. Agar siz XML fayllarini boshqa tizim bilan almashsangiz, shunchaki XSD dan foydalaning. XML formatidagi jOOQ dan INFORMATION_SCHEMA meta-modeliga qarang:
https://www.jooq.org/xsd/jooq-meta-3.10.0.xsd

  • XSD yaxshi tushuniladi
  • XSD XML tarkibini juda yaxshi tokenizatsiya qiladi va barcha mijoz tillarida tekshirish imkonini beradi
  • XSD yaxshi versiyaga ega va rivojlangan orqaga qarab muvofiqlikka ega
  • XSD XJC yordamida Java kodiga tarjima qilinishi mumkin

Oxirgi nuqta muhim. XML xabarlaridan foydalangan holda tashqi tizim bilan aloqa o'rnatishda biz xabarlarimiz haqiqiy ekanligiga ishonch hosil qilishni xohlaymiz. Bunga JAXB, XJC va XSD yordamida erishish juda oson. Xabarlarimizni Java ob'yektlari sifatida yaratadigan "Birinchi Java" dizayn yondashuvi bilan ularni qandaydir tarzda XML bilan uyg'unlashtirish va iste'mol qilish uchun boshqa tizimga jo'natish mumkin deb o'ylash jinnilik bo'lar edi. Shu tarzda yaratilgan XML juda sifatsiz, hujjatsiz va ishlab chiqish qiyin bo'ladi. Agar bunday interfeys uchun xizmat ko'rsatish darajasi bo'yicha kelishuv (SLA) bo'lsa, biz uni darhol buzamiz.

Rostini aytsam, bu JSON API bilan har doim sodir bo'ladi, lekin bu boshqa hikoya, men keyingi safar janjallashaman ...

Ma'lumotlar bazalari: ular bir xil narsa

Ma'lumotlar bazalari bilan ishlashda siz ularning barchasi bir-biriga o'xshashligini tushunasiz. Baza o'z ma'lumotlariga ega va sxemani boshqarishi kerak. Sxemaga kiritilgan har qanday o'zgartirishlar to'g'ridan-to'g'ri DDLda amalga oshirilishi kerak, shunda haqiqatning yagona manbai yangilanadi.

Manba yangilanishi sodir bo'lganda, barcha mijozlar modelning nusxalarini ham yangilashlari kerak. Ba'zi mijozlar jOOQ va Hibernate yoki JDBC (yoki ikkalasi) yordamida Java-da yozilishi mumkin. Boshqa mijozlar Perl da yozilishi mumkin (biz ularga faqat omad tilaymiz), boshqalari esa C# da yozilishi mumkin. Bu muhim emas. Asosiy model ma'lumotlar bazasida. ORMlar yordamida yaratilgan modellar odatda sifatsiz, yomon hujjatlashtirilgan va ishlab chiqish qiyin.

Shunday ekan, xato qilmang. Boshidanoq xato qilmang. Ma'lumotlar bazasidan ishlash. Avtomatlashtirilishi mumkin bo'lgan joylashtirish quvurini qurish. Ma'lumotlar bazasi modelingizni nusxalashni va uni mijozlarga tashlashni osonlashtirish uchun kod generatorlarini yoqing. Va kod generatorlari haqida tashvishlanishni to'xtating. Ular yaxshi. Ular bilan siz yanada samarali bo'lasiz. Siz ularni boshidanoq sozlash uchun ozgina vaqt sarflashingiz kerak - va keyin sizni ko'p yillar davomida samaradorlik kutmoqda, bu sizning loyihangiz tarixini tashkil qiladi.

Keyinroq rahmat aytmang.

Izoh

Aniq bo'lish uchun: Ushbu maqola hech qanday tarzda ma'lumotlar bazasi modelingizga mos kelish uchun butun tizimni (masalan, domen, biznes mantig'i va h.k.) egishingiz kerakligini targ'ib qilmaydi. Men ushbu maqolada aytmoqchi bo'lgan narsa shundaki, ma'lumotlar bazasi bilan o'zaro aloqada bo'lgan mijoz kodi ma'lumotlar bazasi modeli asosida harakat qilishi kerak, shuning uchun uning o'zi ma'lumotlar bazasi modelini "birinchi daraja" holatida takrorlamaydi. Ushbu mantiq odatda mijozingizdagi ma'lumotlarga kirish darajasida joylashgan.

Ba'zi joylarda hanuzgacha saqlanib qolgan ikki darajali arxitekturalarda bunday tizim modeli yagona mumkin bo'lishi mumkin. Biroq, aksariyat tizimlarda ma'lumotlarga kirish qatlami menga ma'lumotlar bazasi modelini qamrab oluvchi "quyi tizim" kabi ko'rinadi.

Istisnolar

Har bir qoida uchun istisnolar mavjud va men allaqachon ma'lumotlar bazasidan birinchi va manba kodini yaratish yondashuvi ba'zan nomaqbul bo'lishi mumkinligini aytdim. Mana bir nechta istisnolar (ehtimol, boshqalar ham bor):

  • Sxema noma'lum bo'lganda va kashf qilinishi kerak bo'lganda. Misol uchun, siz foydalanuvchilarga har qanday diagrammada harakat qilishda yordam beradigan vosita provayderisiz. uf. Bu erda kod ishlab chiqarish yo'q. Ammo shunga qaramay, ma'lumotlar bazasi birinchi o'rinda turadi.
  • Qachonki, ba'zi bir muammolarni hal qilish uchun tez orada sxema yaratish kerak. Ushbu misol naqshning biroz xayoliy versiyasiga o'xshaydi ob'ekt atribut qiymati, ya'ni, sizda, albatta, aniq belgilangan sxema yo'q. Bunday holda, ko'pincha RDBMS sizga mos kelishiga ishonchingiz komil bo'lmaydi.

Istisnolar tabiatan istisno hisoblanadi. RDBMS dan foydalanish bilan bog'liq ko'p hollarda sxema oldindan ma'lum, u RDBMS ichida joylashgan va "haqiqat" ning yagona manbai bo'lib, barcha mijozlar undan olingan nusxalarni olishlari kerak. Ideal holda, siz kod generatoridan foydalanishingiz kerak.

Manba: www.habr.com

a Izoh qo'shish