Trajni podaci s Java Data Objects, 1. dio

"Sve treba učiniti što jednostavnijim, ali ne i jednostavnijim."

Albert Einstein

Potreba za postojanjem podataka stvorenih u vrijeme izvođenja stara je koliko i računarstvo. I potreba za pohranom objektno orijentiranih podataka koji su se pojavili kad je objektno orijentirano programiranje postalo sveprisutnim. Trenutno većina modernih, netrivijalnih aplikacija koristi objektno orijentiranu paradigmu za modeliranje domena aplikacija. Suprotno tome, tržište baza podataka je više podijeljeno. Većina sustava baza podataka koristi se relacijskim modelom, ali objektno-bazirane pohrane podataka pokazuju se neophodnima u mnogim aplikacijama. Osim toga, imamo i naslijeđene sustave s kojima se često moramo povezati.

Ovaj članak identificira probleme povezane s postojanošću podataka u transakcijskim sredinskim softverskim okruženjima, poput J2EE (Java 2 Platform, Enterprise Edition), i prikazuje kako Java Data Objects (JDO) rješava neke od tih problema. Ovaj članak daje pregled, a ne detaljan vodič, a napisan je s gledišta programera aplikacija, a ne dizajnera implementacije JDO.

Pročitajte cijelu seriju o Java Data Objects:

  • Dio 1. Shvatite kvalitete iza idealnog sloja postojanosti
  • Dio 2. Sunce JDO protiv Kastora JDO

Oni Java programeri, dizajneri i J2EE arhitekti koji rade na sustavima koji moraju pohranjivati ​​podatke u relacijske ili objektne baze podataka ili druge medije za pohranu trebali bi pročitati ovaj članak. Pretpostavljam da imate osnovno znanje o Javi i da ste upoznati s objektno-relacijskim pitanjima i terminologijom.

Prozirna upornost: Zašto se truditi?

Više od desetljeća neprekidnih pokušaja premošćivanja objektno orijentiranog vremena izvođenja i postojanosti ukazuje na nekoliko važnih zapažanja (popisanih po važnosti):

  1. Apstrahiranje pojedinosti o postojanosti i imati čist, jednostavan objektno orijentirani API za obavljanje pohrane podataka najvažnije je. Ne želimo obrađivati ​​detalje postojanosti i interno predstavljanje podataka u spremištima podataka, bili oni relacijski, objektno utemeljeni ili nešto treće. Zašto bismo se bavili konstrukcijama niske razine modela pohrane podataka, poput redaka i stupaca, i neprestano ih prevodili naprijed-natrag? Umjesto toga, moramo se koncentrirati na onu složenu prijavu koju smo trebali dostaviti do jučer.
  2. Želimo koristiti plug-and-play pristup s našim pohranama podataka: želimo koristiti različite pružatelje / implementacije bez mijenjanja retka izvornog koda aplikacije - a možda i bez mijenjanja više od nekoliko redaka u odgovarajućoj konfiguracijskoj datoteci ( s). Drugim riječima, potreban nam je industrijski standard za pristup podacima koji se temelje na Java objektima, onaj koji igra ulogu sličnu onoj koju igra JDBC (Java Database Connectivity) kao industrijski standard za pristup podacima temeljenim na SQL-u.
  3. Želimo koristiti plug-and-play pristup s različitim paradigmama baze podataka - to jest, želimo se prebaciti s relacijske baze podataka na objektno orijentiranu s minimalnim promjenama aplikacijskog koda. Iako je lijepo imati, u praksi ta sposobnost često nije potrebna.

    Ovdje je jedan komentar: Iako relacijske baze podataka uživaju najveću prisutnost na tržištu, pružanje jedinstvenog API-ja za trajnost i omogućavanje davatelja usluga pohrane podataka da se natječu u jačinama implementacije ima smisla, bez obzira na paradigmu koju ovi pružatelji koriste. Ovaj bi pristup na kraju mogao pomoći ujednačavanju uvjeta između dviju dominantnih grupa dobavljača baza podataka: dobro ukorijenjenog relacijskog tabora i objektno orijentiranog kampa koji se bori za tržišni udio.

Tri gore navedena otkrića vode nas do definiranja sloja postojanosti, okvira koji pruža Java API visoke razine za objekte i odnose kako bi nadživio životni vijek runtime okruženja (JVM). Takav okvir mora sadržavati sljedeće osobine:

  • Jednostavnost
  • Minimalan upad
  • Transparentnost, što znači da okvir skriva implementaciju pohrane podataka
  • Konzistentni, sažeti API-ji za pohranu / pronalaženje / ažuriranje objekata
  • Transakcijska podrška, što znači da okvir definira transakcijsku semantiku povezanu s trajnim objektima
  • Podrška za upravljana (npr. Na poslužitelju aplikacija), kao i za neupravljana (samostalna) okruženja
  • Podrška za potrebne dodatke, kao što su predmemoriranje, upiti, generiranje primarnog ključa i alati za mapiranje
  • Razumne naknade za licenciranje - nisu tehnički zahtjev, ali svi znamo da loša ekonomija može propasti u izvrstan projekt

Većinu gornjih kvaliteta detaljno opisujem u sljedećim odjeljcima.

Jednostavnost

Jednostavnost je visoko na mojem popisu potrebnih osobina za bilo koji softverski okvir ili knjižnicu (pogledajte uvodni citat ovog članka). Razvoj distribuiranih aplikacija već je dovoljno naporan, a mnogi softverski projekti propadaju zbog loše složenosti (i, šire, upravljanja rizikom). Jednostavno nije sinonim za pojednostavljeno; softver bi trebao imati sve potrebne značajke koje programeru omogućuju da radi svoj posao.

Minimalan upad

Svaki trajni sustav za pohranu unosi određenu količinu upada u aplikacijski kod. Idealan sloj postojanosti trebao bi umanjiti upad da bi se postigla bolja modularnost, a time i plug-and-play funkcionalnost.

U svrhu ovog članka upad definiram kao:

  • Količina koda specifičnog za trajnost raspršena je po kodu aplikacije
  • Potreba za izmjenom vašeg objektnog modela aplikacije bilo da je potrebno implementirati neko sučelje postojanosti - poput Persistableili slično - ili naknadnom obradom generiranog koda

Upad se također odnosi na objektno orijentirane sustave baza podataka i, iako je to obično manji problem u odnosu na relacijske pohrane podataka, može se značajno razlikovati među dobavljačima ODBMS-a (objektno orijentiranog sustava upravljanja bazama podataka).

Transparentnost

Koncept trajne transparentnosti sloja prilično je jednostavan: aplikacija koristi isti API bez obzira na vrstu spremišta podataka (prozirnost tipa pohrane podataka) ili dobavljača spremišta podataka (prozirnost prodavača podataka). Transparentnost uvelike pojednostavljuje aplikacije i poboljšava njihovu održivost skrivajući detalje o implementaciji spremišta podataka u najvećoj mogućoj mjeri. Konkretno, za prevladavajuće relacijske pohrane podataka, za razliku od JDBC-a, ne trebate tvrdo kodirati SQL izraze ili nazive stupaca ili pamtiti redoslijed stupaca koji se vraća upitom. Zapravo ne trebate znati SQL ili relacijsku algebru jer su previše specifični za implementaciju. Transparentnost je možda najvažnija osobina sloja postojanosti.

Konzistentan, jednostavan API

API sloja postojanosti svodi se na relativno mali niz operacija:

  • Osnovne CRUD (stvaranje, čitanje, ažuriranje, brisanje) operacije na prvoklasnim objektima
  • Upravljanje transakcijama
  • Upravljanje identitetima aplikacija i trajnih objekata
  • Upravljanje predmemorijom (tj. Osvježavanje i izbacivanje)
  • Stvaranje i izvršavanje upita

Primjer PersistenceLayerAPI-ja:

javna praznina traje (objekt obj); // Spremi obj u spremište podataka. javno opterećenje objekta (klasa c, objekt pK); // Čitanje obj s danim primarnim ključem. ažuriranje javne praznine (objekt obj); // Ažuriranje modificiranog objekta obj. javno prazno brisanje (objekt obj); // Obriši obj iz baze podataka. nalaz javne zbirke (upit q); // Pronađite predmete koji zadovoljavaju uvjete našeg upita.

Podrška za transakcije

Dobar sloj postojanosti treba nekoliko elementarnih funkcija za pokretanje, predavanje ili vraćanje transakcije. Evo primjera:

// Transaction (tx) demarcation. public void startTx(); public void commitTx(); public void rollbackTx(); // Choose to make a persistent object transient after all. public void makeTransient(Object o) 

Note: Transaction demarcation APIs are primarily used in nonmanaged environments. In managed environments, the built-in transaction manager often assumes this functionality.

Managed environments support

Managed environments, such as J2EE application servers, have grown popular with developers. Who wants to write middle tiers from scratch these days when we have excellent application servers available? A decent persistence layer should be able to work within any major application server's EJB (Enterprise JavaBean) container and synchronize with its services, such as JNDI (Java Naming and Directory Interface) and transaction management.

Queries

The API should be able to issue arbitrary queries for data searches. It should include a flexible and powerful, but easy-to-use, language -- the API should use Java objects, not SQL tables or other data-store representations as formal query parameters.

Cache management

Cache management can do wonders for application performance. A sound persistence layer should provide full data caching as well as appropriate APIs to set the desired behavior, such as locking levels, eviction policies, lazy loading, and distributed caching support.

Primary key generation

Providing automatic identity generation for data is one of the most common persistence services. Every decent persistence layer should provide identity generation, with support for all major primary key-generation algorithms. Primary key generation is a well-researched issue and numerous primary key algorithms exist.

Mapping, for relational databases only

With relational databases, a data mapping issue arises: the need to translate objects into tables, and to translate relationships, such as dependencies and references, into additional columns or tables. This is a nontrivial problem in itself, especially with complex object models. The topic of object-relational model impedance mismatch reaches beyond this article's scope, but is well publicized. See Resources for more information.

The following list of extras related to mapping and/or relational data stores are not required in the persistence layer, but they make a developer's life much easier:

  • A GUI (graphical user interface) mapping tool
  • Code generators: Autogeneration of DDL (data description language) to create database tables, or autogeneration of Java code and mapping files from DDL
  • Primary key generators: Supporting multiple key-generation algorithms, such as UUID, HIGH-LOW, and SEQUENCE
  • Support for binary large objects (BLOBs) and character-based large objects (CLOBs)
  • Self-referential relations: An object of type Bar referencing another object of type Bar, for example
  • Raw SQL support: Pass-through SQL queries

Example

The following code snippet shows how to use the persistence layer API. Suppose we have the following domain model: A company has one or more locations, and each location has one or more users. The following could be an example application's code:

PersistenceManager pm =PMFactory.initialize(..); Company co = new Company("MyCompany"); Location l1 = new Location1 ("Boston"); Location l2 = new Location("New York"); // Create users. User u1 = new User("Mark"); User u2 = new User("Tom"); User u3 = new User("Mary"); // Add users. A user can only "belong" to one location. L1.addUser(u1); L1.addUser(u2); L2.addUser(u3); // Add locations to the company. co.addLocation(l1); co.addLocation(l2); // And finally, store the whole tree to the database. pm.persist(c); 

In another session, you can look up companies employing the user Tom:

PersistenceManager pm =PMFactory.initialize(...) Collection companiesEmployingToms = pm.find("company.location.user.name = 'Tom'"); 

Za relacijske pohrane podataka morate stvoriti dodatnu datoteku mapiranja. To bi moglo izgledati ovako:

    Korisnik Lokacije tvrtke             

Za ostalo se brine postojani sloj koji obuhvaća sljedeće:

  • Pronalaženje ovisnih grupa objekata
  • Upravljanje identitetom aplikacijskog objekta
  • Upravljanje trajnim identitetima objekata (primarni ključevi)
  • Ustrajavanje svakog predmeta u odgovarajućem redoslijedu
  • Pružanje upravljanja predmemorijom
  • Pružanje ispravnog transakcijskog konteksta (ne želimo da postoji samo dio stabla objekata, zar ne?)
  • Pružanje načina zaključavanja po izboru korisnika