Ve třetím díle seriálu jsme vytvořili jednoduchou Java třídu, ke které jsme v díle předchozím vytvořili zrcadlový obraz - persistentní Caché třídu. Jak definice Caché třídy vypadá, jsme měli možnost již vidět, nicméně pro nás v tomto okamžiku není její znalost podstatná. S Caché totiž nebudeme pracovat přímo, ale prostřednictvím objektů Javy. A o těchto objektech si dnes budeme povídat.

Jalapeno poskytuje několik API objektů pro řízení persistence. Jedním z těchto objektů je aplikační kontext reprezentovaný třídou ApplicationContext (com.intersys.pojo.ApplicationContext).

Tato třída implementuje různé možnosti připojení k databázi a k získání instance nejdůležitější API třídy - Správce objektů - ObjectManager (com.intersys.pojo.ObjectManager). Následující krátký kód ukazuje, jak je možno otevřít spojení s databází a instancovat ObjectManager.

String url="jdbc:Cache://" + host + ":1972/SYM";

Class.forName ("com.intersys.jdbc.CacheDriver");
Connection connection = DriverManager.getConnection (url, username, password);
objectManager = ApplicationContext.createObjectManager (connection);

Mimochodem, z příkladu je zřejmé, že připojení ke Caché je realizováno JDBC ovladačem. Ovladač Caché je čistě javovská komponenta, která implementuje jak relační, tak zároveň i objektový přístup k datům uloženým v Caché a ke kódu uloženému formou metod v Caché třídách. Ale o tom opět více v některém budoucím díle.

Máme-li instancován objekt ObjectManager, můžeme volat jeho metody pro manipulování s daty na straně Caché. Například můžeme vložit do databáze nový záznam pomocí metody insert.

import com.intersystems.objects.*;
...
faktura f = new faktura();
...
Id id = (Id)objectManager.insert(f, true);

Druhý argument metody říká, zda se má provést „důsledné" uložení, tedy zda se mají uložit současně i změny objektů na něž odkazuje právě ukládaný objekt. V našem příkladu faktura ukazuje na objekty polozka, tedy určitě budeme chtít spolu s fakturou uložit i její případné položky. Caché zároveň provede validaci čísla faktury, dle anotace uvedené u třídy ucto.faktura:

@Index(name="cislofakturyIndex", isUnique=true, propertyNames={"cislofaktury"})

Následující tabulka podává stručný přehled o několika užitečných metodách správce objektů.

Specifikace

Návratová hodnota

Popis

openById(class,id)

Object

Otevírá objekt zadané třídy podle Id v databázi a vrací referenci na něj

openByPrimaryKey(vlase,primaryKey)

Object

Otevírá instanci zadané třídy podle primárního klíče a vrací referenci na něj

openByQuery(vlase,sql,args[])

java.util.Iterator

Vrací množinu instancí třídy dle zadané SQL podmínky. Na SQL výraz jsou kladeny určité podmínky, jejich popis jde nad rámec tohoto článku

attach(object pojo)

void

Pokud byl objekt připojen ke Caché kopii pomocí např. některé z metod open...(), nebo uložen, či rozpojen, pak se provede připojení. V praxi to znamená, že se vytvoří proxy třída na straně Caché, která provádí změny do databáze. Také to znamená, že hodnoty vlastností třídy jsou z Caché získávány až na vyžádání.

detach(object pojo)

object

Provede odpojení objektu od Caché, tedy od proxy třídy v Caché. To znamená, že veškeré hodnoty vlastností jak samotného objektu, tak objektů provázaných pomocí referencí se přenesou ze serveru Caché do klientské aplikace. Tady ovšem opatrně: může to znamenat někdy velký objem dat a zátěž systému a případně i vést v extrémních případech až k chybě OutOfMemoryError. Klient však má k dispozici data lokálně a nemusí si je vyžadovat nadále ze serveru.

Flush

void

Synchronizuje persistentní kontext s Caché databází

Insert(object pojo,Boolean deep)

Id

Vytvoří novou instanci třídy v Caché a zároveň ji uloží. Paramet deep specifikuje způsob uložení - false: pouze samotná třída, true: třída včetně všech otevřených referencí

Save(object pojo,Boolean deep)

Id

Vytvoří novou instanci v Caché, nebo aktualizuje existující. Rozhodnutí zda se vytvoří nová instance nebo aktualizuje existující se dělá na základě určitých kritérií, viz. dokumentace ke Caché

purgeFromMemory(object pojo)

void

Zruší asociaci mezi Java verzí objektu a proxy třídou v Caché a uzavře proxy třídu na straně Caché. Při použití těsné vazby (o níž opět někdy později...) se zároveň uzavře i Java verze objektu

refresh(object pojo)

void

Provede aktualizaci java objektu dle aktuálního stavu v databázi Caché

removeFormDatabase(object pojo,Id id)

void

Nalezne odpovídající objet v databázi Caché a vymaže jej.

close()

void

Uzavře správce objektů a zneplatní jej

Třída ObjectManager obsahuje též metody pro práci s transakcemi, které zde nebudeme rozebírat, neboť na nich není nic objevného a metody pro instancování dalších objektů Jalapeno API a to jmenovitě Utilities a ExtentManager.

ExtentManager
Tato Třída poskytuje rozhraní pro práci s oblastmi databází na úrovni celých persistentních tříd, tedy nikoliv na úrovni instancí objektů. Její metody lze použít například pro zrušení definice třídy, smazání všech instancí třídy najednou, přepočítání indexů (toto normálně v Caché není třeba, jen pokud programátor změní definici třídy, je nutno uvést hodnoty indexů s jejich definicemi) apod. V neposlední řadě lze použít metodu pro vygenerování pseudonáhodných instancí tříd; to se hodí zvláště potřebujeme-li otestovat aplikaci na velkém objemu dat, která ovšem ne vždy pro testování máme k dispozici z reálného provozu.

ObjectManagerFactory
Tato třída poskytuje rozhraní pro správu připojení ke Caché databázi, např. definuje výchozí hodnoty připojení - url, jméno a heslo a jiná nastavení.

Settings
Tato pomocná třída poskytuje rozhraní pro správu chování třídy ObjectManager. Asi nejdůležitějšími metodami jsou getFetchPolicy a setFetchPolicy. Správné nastavení politiky synchronizace Java klienta se stranou Caché má totiž zásadní vliv na výkon celé aplikace.
Caché podporuje dva typy politik pro synchronizaci a to FETCH_POLICY_EAGER a FETCH_POLICY_LAZY. Pokud zvolíte politiku EAGER, pak v okamžiku instancování POJO objektu se automaticky z databáze načtou hodnoty všech vlastností objektu včetně objektů na něž daný objekt odkazuje a to do všech pokolení odkazů. Toto může vést k přenosům velkého objemu dat a v krajním případě k havárii aplikace z důvodu nedostatku paměti. V druhém případě, při použití LAZY politiky, se sice vytvoří instance Java objektu ale hodnoty vlastností se načítají až na vyžádání, při prvním odkazu na ně.

Utilities
Tato třída implementuje několik metod pro práci s XML dokumenty. Zejména jde o metody pro serializaci do XML a deserializaci z XML do instance POJO objektu.

Nyní si konečně můžeme poskládat celý příklad - zde je jeho kód:

package ucto;

import java.util.*;
import java.io.*;

public class Main {

public Main() {
}

public static void main(String[] args) {
String cisloFaktury;
Random rnd = new Random();

try {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Číslo faktury:");
cisloFaktury = in.readLine();

faktura f = new faktura();
f.polozky = new ArrayList();
f.cislo_faktury = cisloFaktury;
f.prijemce="Jiri Novy";
f.datum_vystaveni= new Date();

polozka p = new polozka();
p.castka = Float.valueOf(rnd.nextInt(1200));
p.popis = "položka číslo 1";
f.polozky.add(p);

polozka pp = new polozka();
pp.castka = Float.valueOf(rnd.nextInt(1800));
pp.popis = "položka číslo 2";
f.polozky.add(pp);

f.castka = p.castka + pp.castka;

System.out.println("Číslo faktury: " + f.cislo_faktury);
System.out.println("Částka celkem: " + f.castka);

DBservice db = new DBservice();
db.ulozFakturu(f,true);
// znicime odkaz na fakturu;
f = null;

System.out.println("Uloženo!");

// upravime fakturu po jejim nacteni z databaze - zmenime prijemce
Iterator faktury = db.FakturaDleCisla(cisloFaktury);
// vime ze bude jen jedna, pokud vubec
...
if (faktury.hasNext()) {
faktura fa = (faktura)faktury.next();
System.out.println("původní příjemce: " + fa.castka);
fa.prijemce = "Moje Firma, s.r.o.";
System.out.println("částka: " + fa.castka);
db.ulozFakturu(fa,false);
}
System.out.println("Stiskni libovolnou klávesu pro dokončení...");
String input = in.readLine();;
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

 

Servisní třída:

package ucto;

import com.intersys.pojo.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.*;
import com.intersys.objects.*;

public class DBservice {

ObjectManager objectManager = null;

/** Creates a new instance of DBservice */
public DBservice() throws Exception {
String host = "localhost";
String username="SYSTEM";
String password="_SYS";
String url="jdbc:Cache://" + host + ":1972/SAMPLES";

Class.forName("com.intersys.jdbc.CacheDriver");
Connection connection = DriverManager.getConnection (url, username, password);
objectManager = ApplicationContext.createObjectManager(connection);
}

protected void ulozFakturu(faktura f, Boolean nova) throws Exception {
Id id = (nova ? (Id)objectManager.insert(f, true): (Id)objectManager.save(f, true));
System.out.println("object id: " + id.toString());
}

Iterator FakturaDleCisla(String cisloFaktury) throws Exception {
String[] args = {cisloFaktury};
return objectManager.openByQuery(faktura.class, "cislofaktury = ?", args);
}
}

Zkušený programátor mi snad promine styl, zde mi jde jen o ukázku a uvedení některých metod API.

Příště nás čeká nová sekce, začneme popisovat spolupráci Caché a Javy z druhé strany, z prostředí Caché.