8.3. Entīšu EJB komponenti

Ievads

Mērķi

  • Saprast entīšu EJB komponentus
  • Zināt galvenās programmēšanas problēmas saistībā ar transakcijām
  • Entītes komponenti

    Entītes komponenti apraksta biznesa objektus, kuru dati glabājas patstāvīgajā datu glabātuvē, kas parasti ir relāciju datubāze. Parasti katram entītes komponentam ir atbilstoša datubāzes tabula. Entītes komponenta klases mainīgie atbilst tabulas kolonām, katra entītes komponenta instance atbilst vienai rindai (jeb ierakstam) datubāzes tabulā. Entītes komponentus var uzskatīt par relāciju-objektu attēlošanas (O/R mapping) standartu.

    Piemērs: Komponenta klase:

    public abstract class AccountEJBBean implements EntityBean {
       public abstract String getKontaNumurs();
       public abstract void setKontaNumurs(String newKontaNumurs);
       public abstract BigDecimal getSumma();
       public abstract void setSumma(BigDecimal newSumma);
    }
    

    Datubāzes tabula:

    Izvietošanas deskriptorā tiek norādīti entītes klases mainīgie, kuru dati tiek glabāti datubāzē, kā arī primārā atslēga.

    <entity>
          <description>Entity Bean ( CMP )</description>
          <display-name>AccountEJB</display-name>
          <ejb-name>AccountEJB</ejb-name>
          <local-home>ejbexample.ejb.AccountEJBLocalHome</local-home>
          <local>ejbexample.ejb.AccountEJBLocal</local>
          <ejb-class>ejbexample.ejb.AccountEJBBean</ejb-class>
          <persistence-type>Container</persistence-type>
          <prim-key-class>java.lang.String</prim-key-class>
          <reentrant>False</reentrant>
          <cmp-version>2.x</cmp-version>
          <abstract-schema-name>AccountEJB</abstract-schema-name>
          <cmp-field>
            <field-name>kontaNumurs</field-name>
          </cmp-field>
          <cmp-field>
            <field-name>summa</field-name>
          </cmp-field>
          <primkey-field>kontaNumurs</primkey-field>
    </entity>
    

    Entītes komponentiem var būt sekojošas alternatīvas:

  • JDO (Java Data Objects). Alternatīvs O/R mapping Java standarts. Piemēram, Oracle TopLink.
  • JDBC (SQL).

Entītes komponentu īpašības:

  • Komponenti ir datubāzes datu attēlojums objektu veidā. Entītes komponenta instances dati tiek sinhronizēti ar datubāzi. 
  • Datu sinhronizāciju var veikt  EJB konteiners (container managed persistence - CMP).
  • Datu sinhronizāciju var veikt EJB klase (bean managed persistence - BMP).  Šajā gadījumā komponenta klasē jāimplementē metodes, kas to dara (parasti ar JDBC izsaukumiem). Šis gadījums šajā kursā netiks apskatīts.
  • Komponentu instances var tik "atrastas". Komponenta home saskarnes metode findByPrimaryKey() izveido komponenta instanci, ja datubāzē eksistē ieraksts ar norādīto primāro atslēgu.
  • Komponentiem parasti ir tikai lokālās saskarnes.
  • Komponentiem var būt relācijas ar citiem komponentiem.

Piemēram, klientam var būt vairāki konti.

public abstract class CustomerEJBBean implements EntityBean {
  public abstract Collection getKonti();
  public abstract void setKonti(Collection newKonti);
}

Relācija ir aprakstīta izvietošanas deskriptorā.

<ejb-relation>
      <ejb-relation-name>Customer-Accounts</ejb-relation-name>
      <ejb-relationship-role>
        <multiplicity>One</multiplicity>
        <relationship-role-source>
          <ejb-name>CustomerEJB</ejb-name>
        </relationship-role-source>
        <cmr-field>
          <cmr-field-name>konti</cmr-field-name>
          <cmr-field-type>java.util.Collection</cmr-field-type>
        </cmr-field>
      </ejb-relationship-role>
      <ejb-relationship-role>
        <multiplicity>Many</multiplicity>
        <cascade-delete/>
        <relationship-role-source>
          <ejb-name>AccountEJB</ejb-name>
        </relationship-role-source>
      </ejb-relationship-role>
</ejb-relation>

Relācijas

Relācijām var būt visas klasiskās kardinalitātes:

  • viens-pret-vienu.
  • viens-pret-daudziem.
  • daudzi-pret-vienu.
  • daudzi-pret-daudziem.

Relācijām var būt virziens:

  • Vienvirziena (unidirectional). 
  • Divirzienu (bidirectional).

Entītes komponenta klases mainīgie var būt trīs tipu:

  • Parasti klases mainīgie.
  • Patstāvīgie lauki (persistent fields). Tie ir lauki, kas tiek saglabāti datubāzē.
  • Relāciju lauki (relationship fields).  Šie lauki satur saistīto entītes komponentu instances.

Patstāvīgie un relāciju lauki ir jānorāda izvietošanas deskriptorā.

Transakcijas

Atomāras darbības

  • Vairākus programmas izpildes soļus var vajadzēt apvienot vienā - nedalāmā jeb atomārā darbībā
  • Piemērs: Naudas pārskaitīšana no viena bankas konta uz citu - naudas atskaitīšanai no viena konta un pieskaitīšana citam kontam ir jānotiek vai nu tā, ka abas darbības ir veiksmīgas, vai (sliktākajā gadījumā) - abas ir neveiksmīgas. "Daļēji izpildīti" pārskaitījumi (piemēram, nauda vienam kontam ir atskaitīta, bet otrā kontā nav nonākusi), nedrīkst būt iespējami.

Naiva programma

Ja apvienojamās darbības ir vienkāršas un to ir nedaudz, var izmantot vienkāršus vadības mehānismus:

try {
    // Atskaita summu no 1. konta
}
catch (Exception e) {
    // Ja notika kļūda, neiet tālāk
    return;
}
try {
    // Pieskaita summu 2. kontam
}
catch (Exception e) {
    // Ja notika kļūda, neiet tālāk
    // un atgriež summu atpakaļ 1. kontā
    return; 
}

Problēmas:

  • Vadības komandu var būt daudz
  • Jāparedz katra kļūda un atbilstoši jāaatritina uzsāktā darbība (rollback of the operation)
  • Kods strauji kļūst sarežģīts, ja atomārā darbība sastāv no daudziem soļiem, kuriem ir iespējami dažādi kļūdu nosacījumi.

Nopietnākas problēmas

Transakcijas nekalpo vienīgi programmēšanas ērtumam, jo dalītās aplikācijās var rasties tīkla, datubāzu utml. traucējumi, kuri transakciju nelietošanas gadījumā noved pie katastrofiskām sekām.

  • Klients var saņemt tīkla darbības izņēmumu, piemēram java.rmi.RemoteException, bet tas parasti nesatur informāciju, vai tīkla kļūda radās pirms, vai pēc naudas noguldījuma.
  • Datubāze var nosprāgt datu rakstīšanas brīdī un tā paliek nekonsistentā stāvoklī. Datubāzes lietotājam ir jāinformē datubāzu serveris, kuras darbības ir jāveic kopā (un jāatrullē).
  • Ja vairāki lietotāji labo to pašu tabulu, viņiem ir jāsinhronizē savas darbības. Ja viņi redz viens otra izveidotos nepabeigtos labojumus, nevar garantēt konsistenci.

No šīm situācijām iespējams izvairīties, pienācīgi izmantojot transakcijas.

Transakciju jēdzieni

Transakciju komponents (transactional object)
Dalītās aplikācijas komponents, piemēram EJB komponents, kurš iniciē transakcijas. Piemēram, "banku operāciju komponents".
Transakciju pārvaldnieks (transactional manager)
Koordinē transakciju komponentu darbību
Resurss (resource)
Pastāvīga atmiņa, kurā var lasīt un rakstīt. Piemēram, datubāze, ziņu rinda vai kas cits
Resursu pārvaldnieks (resource manager)
Piemēram, relāciju datubāzes draiveris. Izplatīts interfeiss resursu pārvaldniekiem ir "X/Open XA" resursu pārvaldības interfeiss.
Sistēma
No transakciju viedokļa ar to saprot kādas aplikācijas resursu kopumu

Transakciju 4 īpašības (ACID)

  1. Atomicity
  2. Consistency
  3. Isolation
  4. Durability
    Atomaritāte (atomicity)
    Transakcijā iesaistītās darbības uzvedas kā viena, nepārtraukta atomārā darbība. Uz atomārām darbībām attiecas likums "visu vai neko" - vai nu visas paredzētās darbības tiek izpildītas, vai arī nenotiek nekādas izmaiņas, ja kaut vienā solī gadījās kļūda. Šajā aspektā transakcijas ir robusts kļūdu apstrādes mehānisms.
    Saskanīgums (consistency)
    Saskanīgums garantē, ka transakcija saglabā sistēmas stāvokļa invariantu. Piemēram, invariants bankas aplikācijā var prasīt: "Naudas daudzums kontā vienmēr ir pozitīvs". Transakcijas vidū šādu invariantu var izjaukt, radot pagaidu nesaskanīgumu, tomēr pēc transakcijas, invarianta prasības atkal atjaunojas. Ārējam sistēmas novērotājam šķiet, ka sistēma VIENMĒR ir nepretrunīgā stāvoklī.
    Izolētība (isolation)
    Vienlaikus izpildošās transakcijas neredz cita citas nepabeigtos (un, iespējams, nesaskanīgos) izmaiņu rezultātus. Katra transakcija tādējādi nejūt citus lietotājus un ir izolēta no citām. To sasniedz, lietojot datubāzē sinhronizācijas protokolus.
    Ilgstamība (durability)
    Garantē, ka resursu izmaiņas pārdzīvo avārijas datubāzē, cietajā diskā, utml. Atjaunojamajiem resursiem ir "log" fails šim mērķim.

Plakanas transakcijas

Pastāv dažādi transakciju modeļi, t.sk. plakanās transakcijas (flat transactions) un saliktās transakcijas (nested transactions). EJB specifikācija prasa, lai būtu plakano transakciju atbalsts.

  • Sākas atomārā darbība, kura sastāv no soļiem, starp kuriem daži rīkojas ar pastāvīgās atmiņas resursiem
  • Beidzot transakciju, rodas viens no diviem rezultātiem - vai nu transakcija tiek komitēta, vai atritināta.

Bieži to uztver kā "nobalsošanas modeli" - ja no daudzajiem soļiem, kuri ir atomārajā darbībā, kaut viens nobalso "pret", tad transakciju atritina.

Saliktas (nested) transakcijas veidotā stāvokļu diagramma drīzāk atgādina sazarotu koku. Kursā sīkāk šo modeli neaplūkosim.

EJB saistība ar transakcijām

EJB komponenti tieši nesadarbojas ne ar transakciju pārvaldnieku, ne ar resursu pārvaldniekiem. Aplikācijas loģika ir augstākā abstrakcijas līmenī. EJB komponenti toties var nobalsot par to, vai transakciju vajag atritināt.

Svarīgi zināt, kurš uzsāk transakciju, kurš izraisa komitēšanu vai atritināšanu, un kad tas notiek. Šīs darbības var uzskatīt par transakcijas robežu nospraušanu (demarcating transactional boundaries). EJB tehnoloģijās ir iespējami 2 veidi, kā nospraust transakciju robežas:

  • Programmējamās transakcijas: EJB komponents pats izsauc vispirms "begin()" un tad "commit()" vai "abort()" komandas
  • Deklaratīvas transakcijas: EJB konteiners pats nodarbojas ar transakcijām un EJB komponentam deleģē tikai aplikācijas loģiku

Ja lietojam deklaratīvas transakcijas, tad deskriptorā src/metadata/ejb-jar.xml attiecīgajam sesijas EJB komponentam rakstām sekojošo.

<ejb-jar>
  <enterprise-beans>
    ...
    <session>
      <description>Session Bean ( Stateless )</description>
      <display-name>BankSessionEJB</display-name>
      <ejb-name>BankSessionEJB</ejb-name>
      <home>ejbexample.ejb.BankSessionEJBHome</home>
      <remote>ejbexample.ejb.BankSessionEJB</remote>
      <ejb-class>ejbexample.ejb.BankSessionEJBBean</ejb-class>
      <session-type>Stateless</session-type>
      <transaction-type>Container</transaction-type>
          ...
    </session>
    ...
  </enterprise-beans>
  ...
</ejb-jar>

Tā kā entītes EJB komponenti uztic datu ielādi un ierakstīšanu EJB konteineram, tad tiem vienmēr jālieto deklaratīvās transakcijas - t.i. arī transakciju veikšanu jāuztic konteineram.

Piemērs

Sesijas EJB komponents lieto deklaratīvās transakcijas. Kāda izskatās viena atomāra darbība?

public void deposit(double amt) throws AccountException {
    System.out.println("deposit(" + amt + ") called."); 
        balance += amt;
}

Kas jādara, lai tekošo transakciju piespiestu atritināties? Piemēram, ja ir aizliegts noguldīt 1,000,000.00 vai vairāk. Vai pietiek ar to, ka metam šajā klasē AccountException? Analoģisks kods, kurš izmanto programmējamās transakcijas:

public void deposit(double amt) throws AccountException {
  javax.transaction.UserTransaction userTran = null; 
  try {
    System.out.println("deposit(" + amt + ") called."); 
    userTran = ctx.getUserTransaction(); 
        userTran.begin(); 
    balance += amt;
    userTran.commit(); 
  }
  catch (Exception e) {
    if (userTran != null) userTran.rollback();
    throw new AccountException("Deposit failed because of " + 
      e.toString());
  }
}

Šo otro variantu mēs saprotamu iemeslu dēļ šajā kursā nelietosim.

Transakciju atribūts

  • Required
  • RequiresNew
  • Supports
  • Mandatory
  • NotSupported
  • Never

No šiem izmantojam "Required"

Transakciju izolācijas līmeņi

Ir sekojošas nevēlamas situācijas (sākot ar pašām nevēlamākajām):

Netīrs nolasījums (dirty read)
Transakcija A labo rakstu datubāzē, transakcija B nolasa šo rakstu, transakcija A atritinās, atgriežoties sākumstāvoklī. Rezultātā B iegūst nepareizus datus, kuri nekad nav bijuši komitēti.
Neatkārtojams nolasījums (unrepeatable read)
Transakcija A nolasa ierakstu, transakcija B labo šo ierakstu, tad transakcija A (piemēram, ar cita veida pieprasījumu) nolasa šo rakstu vēlreiz. Rezultātā A ir ieguvusi divas dažādas viena raksta vērtības.
Fantomu nolasījums (phantom read)
Transakcija A dod vaicājumu datubāzei ar meklēšanas kritēriju (WHERE apakšizteiksmi); pēc tam transakcija B izveido jaunus rakstus, kuri atbilst šim kritērijam. Visbeidzot A atkārto savu vaicājumu. Rezultātā A 2. vaicājumā ir nolasījusi jaunos - fantomu ierakstus, lai gan vecajiem ierakstiem vērtības nav mainījušās.

Transakciju izolācijas līmeņi un iespējamās problēmas

Izolācijas līmenisNetīri nolasījumi?Neatkārtojami nolasījumi?Fantomu nolasījumi?
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE