J2EE CMP Entity Beans, Transaksjoner, JSP
CMP Entity Beans Container Managed Persistence Container sin oppgave å lagre innholdet i EJB til varig lager (typisk DB). Implementasjonsklassen lages abstrakt. container lager sub-klasse som implementerer metodene ejbload() ejbstore() Har ikke objektvariabler kontonr og saldo => bare abstrakte get/set-metoder for variablene Forretningslogikk metodene bruker get/setmetodene for å endre dataene i EJB en.
Impl. KontoCMPBean import javax.ejb.*; import java.util.*; public abstract class KontoCMPBean implements EntityBean{ private EntityContext ctx; /**************** SET/GET-metoder ********************/ public abstract void setkontonr(integer kontonr); public abstract Integer getkontonr(); public abstract void setfornavn(string fornavn); public abstract String getfornavn(); public abstract void setetternavn(string etternavn); public abstract String getetternavn(); public abstract void setsaldo(double nysaldo); public abstract int getsaldo(); /************* ENTITY METODER **********************/ public void setentitycontext(entitycontext ctx){ this.ctx = ctx; public Integer ejbcreate(integer kontonr, String fornavn, String etternavn) throws CreateException{ if (kontonr == null kontonr.intvalue() < 1) throw new CreateException("Kontonr må være et positivt tall"); if (fornavn == null fornavn.equals("")) throw new CreateException("Eier av konto må ha et fornavn"); if (etternavn == null etternavn.equals("")) throw new CreateException("Eier av konto må ha et etternavn"); setkontonr(kontonr); setfornavn(fornavn); setetternavn(etternavn); setsaldo(0); return null; //gjøres når det er snakk om CMP. Containeren lager nøkkelen
forts. KontoCMP public void unsetentitycontext(){ public void ejbpostcreate(integer kontonr, String fornavn, String etternavn){ public void ejbload(){ public void ejbstore(){ public void ejbactivate(){ public void ejbpassivate(){ public void ejbremove(){ /******* Foretningslogikk ***********************/ public void settinn(int belop){ int saldo = getsaldo(); setsaldo(saldo + belop); public void taut(int belop) throws KontoException{ int saldo = getsaldo(); if (saldo - belop < 0) throw new KontoException("Ikke dekning på konto"); setsaldo(saldo - belop); public int hentsaldo(){ return getsaldo();
Finn-metodene public interface KontoCMPHome extends EJBHome{ public KontoCMP create(integer nyttkontonr, String fornavn, String etternavn) throws RemoteException, CreateException; public KontoCMP findbyprimarykey(integer kontonr) throws RemoteException, FinderException; public Collection findbysaldooverbeløp(int saldo) throws RemoteException, FinderException; //home-metode public int beregnantallkontoer() throws RemoteException, Beskrives i Home-interfacet (som vanlig) ingen implementasjon findbyprimarykey(integer kontonr) findbysaldooverbeløp(int saldo) findallekontoer() ikke med over
Finnmetoder Hvordan kan container lage spørring for findbysaldooverbeløp()? må ha hjelp av deg, til å spesifisere spørringen. container returnerer alltid en/flere EJB er som resultat til finn-metodene! For alle finn metoder spesifiserer vi EJB- SL spørringer i dd (via deploytool).
dd <guery> <query-method> <method-name>findbysaldooverbeløp</method-name> <method-params> <method-param>int</method-param> </method-params> </query-method> <ejb-ql>select object(o) from KontoSkjema o where o.saldo?1</ejb-ql> </query> Klient findbysaldooverbeløp(2) Home- Obj. SQL:select. Prim.nøkkel: 2,5,6 DB Collection EJB-obj. Prim.nøkkel 2 EJB-obj. Prim.nøkkel 5 EJB-obj. Prim.nøkkel 6
Home-metoder Ønsker å finne ut hvor mange kontoer som finnes. Hvordan gjøres dette? Collection findallekontoer() //finnmetode int beregnantallkontoer() //home-metode Home-metoder beskrives i home-interface + implementasjon i impl.klassen. Metode som ikke er knyttet til en spesiell instans (sees på som klassemetode)
Home-metode Home-interfacet int beregnantallkontoer() throws RemoteEx. Impl-klassen int ejbhomeberegnantallkontoer() Hvordan beregner vi hvor mange kontoer som finnes? må gjøre spørring mot DB, men ikke finnmetode
Select-metoder Ved å lage metoder som begynner med ejbselect i impl. klassen angir du at dette er en metode det skal kobles en EJB-QL spørring mot! Collection ejbselectallekontoer() int ejbhomeberegnantallkontoer(){ Collection kontoer = ejbselectallekontoer(); return kontoer.size();
Forhold mellom Entity Beans Person 1 0..* Konto
Databasetabeller for EJB er Personnummer Fornavn Etternavn 231288 45254 Ola Normann 010199 11111 Kari Normann Kontonr Saldo Fremmednøkkel 1 2 3 1000 2000 1 231288 45254 231288 45254 010199 11111 Ola har to kontoer. Fremmednøkkel ligger i mange delen av relasjonen. Hvordan kan container lage tabellene automatisk? Vi må angi forholdet/relasjonen mellom EJB ene (deploytool).
Problemer med distribuerte transaksjoner Vi skal overføre penger mellom en konto i DNB og SB1 => to DB-systemer overfør(int frakonto, tilkonto, beløp){ try{ //sørg for at autocommit er false forbindelsednb = //hent DB-forbindelse forbindelsesb1 = //hent DB-forbindelse forbindelsednb.taut(beløp); forbindelsesb1.settinn(beløp);.. catch(..) Problemet ligger i at vi får to transaksjoner her. Databasesystemene kan ikke hjelpe oss slik at overføringen blir en transaksjon (i stedet for 2). => Overføringen er ikke automisk. Det blir ikke alt eller ingenting her. En av transaksjonene kan gå mens den andre feiler.
To-fase commit. Bruker applikasjonstjeneren som sjef/monitor i transaksjonen. Objektene som inngår i transaksjonen rapporterer til tjeneren. public void overfør(int frakonto, int tilkonto, beløp){ //start transaksjon, alt som kommer etter ligger i en transaksjon //finn objektet (EJB) for frakonto //finn objektet (EJB) for tilkonto //gi objektet for frakonto beskjed om å sjekke om det er mulig å trekke beløp //gi objektet for tilkonto beskjed om å sjekke om det er mulig å sette inn beløp //hvis alt OK så gir applikasjonstjeneren beskjed om å utføre handlingen, dvs. commit. //hvis ikke gi objektene beskjed om å ikke utføre endringer => rollback()
Fasade av Session Beans Metoder i Entity Beans er alltid i en transaksjon (enten alene eller som del av en annen transaksjon). Spesifiseres i deploytool Containeren sørger for utførelsen I en Session Bean er det valgfritt om en metode skal være en transaksjon eller ikke. Spesifiseres i deploytool Container kan sørge for utførelsen (kan også gjøres manuelt).
Klient taut(100) KontoCMP Saldo = 100 (0) settinn(100) KontoCMP Saldo = 100 (200) DB Container overfør(,100) Klient Session Bean overfør(){ starttrans taut(100) KontoCMP Saldo = 100 (0) DB slutttrans settinn(100) KontoCMP Saldo = 100 (200)
Isolering mellom transaksjoner DB Konto 1 (EJB) Konto 1 (EJB) 100 ejbload() => saldo = 100 100 saldo = saldo + 100 100 ejbload() => saldo = 100 200 200 ejbstore() => saldo = 200 saldo = saldo + 100 200 ejbstore() => saldo = 200
Kommentar isolering Problemet ligger i at to transaksjoner (EJB er) får tilgang til å lese innholdet i DB-samtidig. Vi må altså spesifisere hvordan databasesystemet skal forholde seg til samtidighet READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ SERIALIZABLE (transaksjoner utføres i serie, liten samtidighet). To/tre måter å unngå dette: 1. forbindelse.settransactionisolation(int level) 2. Sett isolering på databasenivå. Dette gjøres da via databaseadministrasjonsverktøy. For cloudscape kan man for eksempel gjøre slik <isql> CALL PropertyInfo.setDatabaseProperty( 'cloudscape.language.defaultisolationlevel, 'SERIALIZABLE'); 3. Konfigurasjon i applikasjonstjeneren (best løsning) Metode 1 fungerer ikke sammen med containerens transaksjonshåndtering! Man skal aldri endre isolasjonsnivå i en transaksjon!! overfør(){ //container starter transaksjon forbindelse.settransactionisolation(.) //endring inne i transaksjon!!
Unntak Hiv EJBException() i EJB-metodene setentitycontext() ejbload() ejbstore() osv.. I andre metoder er det ofte best å hive egne unntak (lag egne unntaksklasser). EJBException() gjør at eventuelle transaksjoner rulles tilbake. Andre unntak gjør ikke dette => må gjøres manuelt overfør( ){ try{ //finn EJB for frakonto //trekk beløp; hiver KontoException hvis det ikke er dekning //finn EJB for tilkonto //sett inn beløp catch(kontoexception e){ kontekst.setrollbackonly(); //VIKTIG
J2EE og web J2EE-applikasjoner kan bestå av JSP-klienter i stedet for java-klienter eller begge deler. gjøres enkelt i deploytool Gjør at fordelene fra J2EE kan brukes også i forbindelse med web. Komponentene blir de samme uansett hvilken klient vi bruker. Gjenbruk. Jsp: <%.. InitialContext init = new InitialContext(); Object obj = init.lookup("java:comp/env/ejb/gjetttallspill"); GjettTallSpillHome home = (GjettTallSpillHome)PortableRemoteObject.narrow(obj, GjettTallSpillHome.class); spill = home.create(); session.setattribute("spill", spill); %>
Verktøy for J2EE Utviklingsverktøy Jbuilder Sun ONE Enterprise osv Applikasjonstjenere WebLogic (kommersiell) Jboss (open source) osv. Ant Verktøy for å kompilere kode m.m. (deploy) Uansett verktøy => prinsippene blir de samme Husk at ear/jar-filene dere lager skal være universelle. De skal fungere uansett hvilken tjener man velger.