středa 4. listopadu 2009

Inicializace databázových dat prostředky Javy

Končí inicializace databáze vytvořením tabulek a integritních omezení? Jak ji naplnit nezbytnými daty? Téměř každá aplikace potřebuje k práci nějaké ty číselníky, role, přístupová práva, uživatele a další data. V tomto blogu zkusím popsat, co může inicializace dat v relační databázi znamenat a jak pro ni výhodně použít prostředky Javy.

Článek je psán s ohledem na běžný projekt, který pracuje nad relačními daty (ne nad objektovou databází, ne nad geografickými daty a pod.), kde objem inicializačních dat není extrémně velký (nebudu popisovat inicializaci dat v obrovském datovém centru), kde v databázi není implementovaná žádná logika a kde si persistenci řeší aplikace (ORM, sql, ...).

Budu předpokládat, že už máme založenou databázi a v ní vytvořený datový model. Vytvoření datového modelu může být sama o sobě zajímavá otázka, hlavně s ohledem na jeho vývoj, aktualizace a udržování změn. Dnes mě ale bude zajímat pouze inicializace dat. Co od ní můžeme požadovat?
  • Přizpůsobitelnost - Můžeme si představit aplikaci, která je instalovaná u několika zákazníků, typicky mnoha. O datech pak můžeme mluvit jako o obecných a zákaznicky specifických. Jak inicializovat zákaznicky specifická data? Je dobré mít inicializaci dat rozdělenou na inicializaci těch obecných a s ní konzistentní způsob na inicializaci zákaznických rozšíření.
  • Nezávislost na konkrétní databázi - Jsou aplikace, typicky produktová řešení, které je možné provozovat na různých databázích (ve smyslu Oracle, MySql, ...). Nemusí to být vždy možné, ale určitě by bylo výhodné mít společný způsob inicializace dat použitelný pro každou z nich.
  • Použitelnost pro testy - Unit testy vyžadující databázi také potřebují mít inicializovaná data. Typicky vyžadují podmnožinu z dat produkčních (číselníky...) a nějaká data specifická pro testy. Navíc, různé testy mohou potřebovat různá testovací data. Různé testy si tak můžeme představit jako různé zákazníky a testovací data rozdělit na obecná a specifická pro konkrétní test. Bylo by hezké mít jeden způsob, jak přistoupit k inicializaci produkčních i testovacích dat.
  • Testovatelnost - Také v datech mohou být chyby. Bylo by dobré mít způsob, jak správnost inicializace dat otestovat.
  • Verzování a správa změn - Tak, jak se vyvíjí aplikace, mohou se vyvíjet i data, se kterými pracuje. Přirozeným požadavkem se zdá být možnost verzování změn inicializačních dat.
  • Přehlednost a udržovatelnost - Stejně jako zdrojové kódy nebo jiné artefakty, i data a jejich inicializace chceme mít dobře organizovaná a inicializační proceduru přiměřeně jednoduchou a dlouhodobě udržovatelnou. 
  • A další... Samozřejmě existuje spousta dalších kritérií, která můžeme po inicializaci požadovat. Snažil jsem se shrnout ta, která jsou zajímavá pro další text.

Jak se inicializace dat řeší? Shrnu ty způsoby, které jsem si na projektech vyzkoušel a které mi přijdou jako nejčastěji používané.

Dump celé databáze

Dost často se databáze průběžným vývojem přivede do stavu, který odpovídá požadavkům na produkční prostředí, a pak se pořídí její dump. Ten se v cílovém prostředí celý naimportuje. Problémem může snaha o podporu více databází - jak ve smyslu Oracle, MySql, tak podle zákazníků. Různé varianty vyžadují existenci různých dumpů, tedy i databází, ze kterých dump vytvoříme. Naopak výhodou je rychlost importu dat. Otázkou je, nakolik je pro nás rychlost inicializace důležitá. Dumpy se často kombinují s sql skripty, které databázi po importu dumpu aktualizují. Přehlednost, udržovatelnost, použitelnost pro testy, verzování, ... také vidím jako obtížné.

Sql skripty

Data můžeme do databáze přirozeně vložit sql skripty. Typicky ale nestačí "dlouhý seznam insertů", skripty mohou vypadat o dost složitěji. U složitěji provázaných dat (na úrovni doménového modelu bych řekl "grafů objektů") mohou skripty obsahovat hodně práce s proměnnými, cykly, někdy i funkce nebo celé balíčky funkcí. Zajímá mě inicializace dat na projektu, kde si perzistence řeší aplikační vrstva a taková spousta sql kódu jenom pro inicializaci dat je tedy nechtěná. Přehlednost a udržovatelnost sql skriptů také stojí hodně úsilí.

Speciální nástroj na správu a migraci dat

Existují nástroje, které inicializaci a obecně správu a migraci dat komplexně řeší. Příkladem může být třeba Embarcadero Change Manager. Vedle ceny může být u takových nástrojů limitující i způsob, jak práci s nimi zaintegrovat s našimi dalšími nástroji (IDE, maven, ...) a vývojovým procesem. Příklad nebo hodnocení komerčních nástrojů je ale mimo rozsah tohoto článku.

Java + ORM

Na několik posledních projektech jsme inicializaci dat prováděli z prostředí Javy. Máme-li doménový model, který data plně popisuje, můžeme libovolná data instancemi tříd z doménového modelu reprezentovat (od toho doménový model máme, že...). Inicializace dat se tak převedla na inicializaci doménového modelu. A to už je otázka pro Javu, kde to umím řešit přehledně a dlouhodobě udržovatelně a to i pro velmi složité "grafy objektů". Dobrý nápad je zestručnit výřečný Java kód pro inicializace použitím nějakého dynamického jazyka, výhodné může být třeba Groovy.

Pro řešení persistence na projektech používáme nejčastěji Hibernate, máme tedy pro doménový model vytvořené mapování. Zapsat instance tříd z doménového modelu do databáze je potom triviální úkol. Řeším inicializaci a chci tedy řešit hlavně "data", algoritmus zápisu mi kód nebude znepřehledňovat.

Podle některých učebnic by asi inicializace dat neměla záviset na kódu, který data posléze bude využívat, ale v závislosti na doménovém modelu a mapování nic špatného nevidím. Pokud na projektu není použité ORM a persistenci řeší DAO třídy třeba s využitím JDBC nebo jakkoliv jinak, ani závislost na DAO vrstvě nemusí být problém.

Použití ORM je výhodné i v případech, kdy se vytváří nějaké produktové řešení a je nutnost podporovat více databází. Hibernate se svými dialekty mi proto velmi vyjde vstříc.

Pro některé typy dat může být výhodné reprezentovat jejich inicializační hodnoty mimo Java kód. Řešíme tak třeba číselníky. Máme je reprezentované jednoduchým XML formátem a v Javě napsaný jeho parser a konvertor do doménového modelu. XML je výhodné i v tom, že obsah XML souborů může generovat nebo kompilovat z různých zdrojů automaticky.

Shrnutí

Popsané způsoby a jejich vztah k požadavkům na inicializaci dat si můžeme shrnout v následující tabulce.


Přehlednost a udržovatelnost
Přizp.
Nezávislost na konkrétní db
Použitelnost pro testy
Testovatelnost
Verzování a správa změn
Dump
subjektivní,
imho ne
obtížně
různé dumpy pro různé db
obtížně
ano
obtížně
Sql
subjektivní,
imho ne
obtížně
různé skripty pro různé db
obtížně
ano
ano
Java + ORM mapování
subjektivní,
imho ano
ano
ano
ano
ano
ano

Tabulka samozřejmě přináší značně subjektivní zhodnocení, ale ukazuje, proč dávám přednost variantě s Javou a mapováním.

Článek neměl ambici encyklopedicky popsat všechny možné způsoby inicializace. Chtěl jsem shrnout ty přístupy, se kterými jsem se setkal a vysvětlit, proč mi přijde výhodné provádět inicializaci prostředky Javy. Jako každé řešení to má své výhody i nevýhody. Myslíte, že je inicializace dat prostředky Javy dobrý nápad? Používáte podobný postup taky? Podělte se o své názory a nápady v diskuzi pod článkem.

V příštím blogu se podívám na to, jak přistoupit k založení datového modelu a jak zvládnout jeho vývoj a aktualizace včetně aktualizací dat. Představím také zajímavý nástroj LiquiBase.