V tomto článku plynulo prejdeme na druhý spôsob možnej implementácie entitných EJB objektov, ktoré predstavujú možnú alternatívu. Namiesto kódovania všetkých prístupových metód potrebných na ukladanie stavu entity budeme v tomto prípade vytvárať deklaratívne mapovania a necháme EJB kontajner, aby vytvoril potrebné JDBC volania za nás. V prípade CMP sa síce vzdávate určitej flexibility, ktorú ste mali s BMP, ale na strane druhej sa vzdávate aj nutnosti objemnejšieho kódovania.

V prípade CMP (Container-Managed Persistence) ste ale zodpovední za identifikovanie atribútov perzistencie danej entitnej triedy a za definovanie jej vzťahov s inými entitami, ale zároveň nemusíte písať žiadny kód na prístup k databáze. V porovnaní s BMP (a staršími verziami CMP) je najpodstatnejšou zmenou pri deklarovaní entitnej EJB triedy to, že ju musíte deklarovať ako abstraktnú. Je to dané tým, že všetku zodpovednosť za zabezpečenie a realizáciu perzistencie prenechávate EJB kontajneru, no a ten potom môže zase vám poskytnúť konkrétnu implementáciu vašej abstraktnej triedy.

Zodpovednosť vás, ako tvorcov beanov, je v implementovaní definovanej biznis logiky a v identifikovaní polí a vzťahov, ktoré musia byť perzistentné pre konkrétnu entitu, ale tu vaša práca končí. Je na EJB kontajneri, ako implementuje perzistentnosť, ktorú vaša entitná trieda vyžaduje.

Okrem toho, že vaša entitná trieda je abstraktná, musí spĺňať ešte nasledovné:

  • byť deklarovaná ako public
  • implementovať EntityBean rozhranie
  • poskytovať verejný, bezparametrický konštruktor (najlepšie nedefinovať v tomto prípade žiadny)
  • v žiadnom prípade neimplementovať metódu finalize()

Definovanie CMP polí

Dôvod, prečo musíte deklarovať CMP entitnú triedu ako abstraktnú, je ten, že identifikujete jednotlivé perzistentné polia a vzťahy s použitím abstraktných metód. Nie ste zodpovední za deklarovanie žiadnej premennej inštancie, ktorá by reprezentovala dané pole alebo držala referenciu na iný objekt, pretože toto sú už implementačné detaily ponechané na kontajner.

Toto je naozaj podstatná zmena v porovnaní s BMP, preto by možno bolo vhodné pozrieť sa na nejaký príklad a porovnať jednotlivé prístupy. Nasledujúci kód obsahuje segment lokálneho rozhrania zadefinovaného pre náš príklad on-line aukčného systému z predchádzajúcich článkov:

public interface EnglishAuction extends EJBLocalObject {
 //…
 public void setName(String newName);
 public String getName();
 public void setDescription(String newDescription);
 public String getDescription();
 //…
}

Neskôr sme si predstavili BMP implementáciu entitnej triedy EnglishAuctionBean, ktorá podporovala množstvo biznis metód, ktoré používali polia inštancie deklarované pre túto triedu, napríklad:

public class EnglishAuctionBean extends AbstractEntity implements EntityBean {
 //…
 protected String name;
 protected String description;
 //…
}

Tieto polia inštancie umožnili implementovať biznis metódy v komponentnom rozhraní:

public void setName(String newName) {
 name = newName;
}
public String getName() {
 return name;
}

Toto všetko už máme za sebou, ale prístup pri deklarovaní perzistentných CMP polí je úplne odlišný. Napríklad CMP implementácia entitnej triedy EnglishAuctionBean by mohla deklarovať pole s názvom name použitím nasledovnej deklarácie abstraktnej metódy:

public abstract class EnglishAuctionCMPBean extends AbstractEntity implements EntityBean {
 //…
 public abstract void setName(String newName);
 public abstract String getName();
 //…
}

V tomto prípade je name iba virtuálne pole dostupné cez deklarované get/set metódy. Bolo by ilegálne deklarovať premennú inštancie s názvom name dostupnú cez metódy getName() a setName(), pretože je naozaj na kontajneri, ako vnútorne realizuje implementáciu virtuálnych polí, označovaných pojmom CMP polia.

Kontajner obvykle použije reflexiu a jednotlivé položky deployment deskriptora, aby identifikoval tieto CMP polia. Ak by ste sa pokúsili skutočne deklarovať pole name spoločne so spomenutými metódami, výsledkom by bola chyba pri preklade.

Teda CMP polia sú nástroj, ktorým je možné spravovať perzistentné atribúty entitnej triedy. Každé toto pole musí udržiavať buď niektorý z Java primitívnych typov, alebo serializovateľný objekt. V prípade aukčného systému to môže byť napríklad názov aukcie, jej popis alebo minimálny vklad. CMP polia nemôžu udržiavať referencie na iné entitné objekty.

Napriek tomu, že get/set metódy CMP polí musia byť definované ako public, v skutočnosti nie sú tieto metódy dostupné pre klienta. Sú dostupné iba samotnému kontajneru. Je to však možné zmeniť tým, že zahrniete get/set metódy CMP poľa do komponentného rozhrania entitnej triedy. Vieme totiž, že klient nepristupuje k inštancii entity priamo, ale cez jej rozhrania. Platí tu však jedno dôležité obmedzenie – klientovi nemôžete vystaviť set metódy, ktoré manipulujú s primárnym kľúčom entitnej triedy. Dôvodom je to, že EJB kontajner neumožní zmeniť primárny kľúč potom, ako bol priradený entite.

Hoci je takto možné dať k dispozícii klientovi aj set CMP metódy, ktoré nie sú asociované s primárnym kľúčom, obvykle tak neurobíte. Problém s priamym prístupom k týmto metódam spočíva v tom, že nemôžete do nich implementovať žiadnu vami požadovanú biznis logiku (napríklad validácie), pretože keď EJB kontajner implementuje vaše CMP abstraktné metódy, jediné, čo zabezpečí, je implementácia perzistencie daného virtuálneho poľa. Tento problém sa dá pomerne jednoducho obísť tak, že si vytvoríte „pomocné“ set metódy, v ktorých implementujete požadovanú biznis logiku, a v rámci týchto metód zabezpečíte volanie metód, ktoré aktualizujú CMP polia.

Nasledovná ukážka predstavuje spomínané riešenie problému s aktualizáciou CMP polí:

public abstract class EnglishAuctionCMPBean
  extends AbstractEntity
  implements EntityBean {
 //…
 public abstract void setStartingBidField(Double newStartingBid);
 public abstract Double getStartingBidField();
 public void setStartingBid(Double newStartingBid)
  throws InvalidAuctionStatusException {
  if ((getStatusField() == null) |
   IAuctionStatus.AUCTION_PENDING.equals(getStatusField())) {
   setStartingBidField(newStartingBid);
  }
  else {
   throw new InvalidAuctionStatusException(
    „Can only set the starting bid for a pending auction“);
  }
 }
 public Double getStartingBid() {
  return getStartingBidField();
 }
 //…
}

Definovanie CMR polí

Pri použití CMP prístupu môže EJB kontajner spravovať vzťahy medzi entitami za vás. Avšak určitým obmedzením je, že môžete takto definovať vzťahy iba medzi entitami implementujúcimi EJB 2.0 CMP prístup a zároveň musia byť tieto entity definované v tom istom deployment deskriptore. Jednotlivé vzťahy medzi entitami môžu byť typu O-O (one-to-one), O-M (one-to-many) alebo M-M (many-to-many) a môže ísť o vzťahy obojsmerné alebo jednosmerné.

Pri obojsmernom vzťahu môže akákoľvek entita komunikovať s akoukoľvek inou, ak sú medzi nimi zadefinované vzťahy. Pri jednosmernom vzťahu môže ísť logicky o komunikáciu iba jedným smerom. Všetky vzťahy sú definované z pohľadu lokálnych rozhraní, čiže každá entita, ktorú chceme použiť ako cieľ komunikácie medzi entitami, musí mať definované lokálne rozhranie. Entita bez lokálneho rozhrania môže byť použitá iba pri jednosmernom vzťahu a musí vystupovať ako iniciátor komunikácie.

Z logického hľadiska má teda jednosmerná komunikácia zmysel iba vtedy, ak má len jedna strana vedieť o tej druhej (druhých). Napríklad on-line aukcia potrebuje vedieť všetko o existencii položiek, ktoré ponúka, ale tieto položky nemusia principiálne vedieť, v akej aukcii sú ponúkané.

Nasledujúca ukážka ilustruje spôsob ako deklarovať vzťah s entitnou triedou Item:

public abstract class EnglishAuctionCMPBean
 extends AbstractEntity implements EntityBean {
 //…
 public abstract void setItem(Item newItem);
 public abstract Item getItem();
 //…
}

Uvedená deklarácia definuje Container-Managed Relationship (CMR) pole, ktoré asociuje položku aukcie s aukciou samotnou. CMR pole je definované úplne rovnakým spôsobom ako CMP pole, čiže použitím abstraktných get/set metód. Entita referencovaná cez jej lokálne rozhranie je v tomto prípade previazaná vzťahom one-to-one. Ostatné dva typy vzťahov, one-to many a many-to-many sa definujú s využitím kontajnerov na objekty, ako je Collection a Set. Takže napríklad one-to-many vzťah medzi zákazníkom a jeho objednávkami môže byť definovaný nasledujúcimi dvoma metódami:

public abstract void setOrders(Collection newOrders);
public abstract Collection getOrders();

Ak potrebujete použiť entitu asociovanú s inou entitou, urobíte tak jednoducho zavolaním príslušnej get metódy. Všetka práca s tým spojená je následne už úlohou EJB kontajnera. Je to ekvivalent tomu, ako keby ste použili vyhľadávaciu metódu na nájdenie local home rozhrania danej entity (respektíve kolekcie entít).

Na záver ešte jedna dôležitá pripomienka. CMP a CMR polia pre jednotlivé entity sú identifikované v deployment deskriptore a nie iba jednoduchým vyčítaním z názvu get/set metód. Výhodou toho je to, že môžete zadefinovať aj vlastné biznis get/set metódy bez toho, aby prišlo k zámene s CMP alebo CMR poliami.

Žádný příspěvek v diskuzi

Odpovědět