J2EE og distribuerte systemer Leksjon 10: Entity Beans (BMP)



Like dokumenter
J2EE. CMP Entity Beans, Transaksjoner, JSP

J2EE og distribuerte systemer Leksjon 11: Entity Beans (CMP)

Repetisjon J2EE. Fullstendig skisse. JNDI og EJB. Session Beans Entity Beans (BMP)

J2EE og distribuerte systemer Leksjon 9: Session Beans

Løsningsskisse, eksamen J2EE og distribuerte systemer 19.mai 2004

Videregående programmering 6

HØGSKOLEN I SØR-TRØNDELAG

La oss først se på problemet med objektorientert tankegang. Se figuren under. Konto

J2EE. Katalogtjenester, JNDI og Enterprise Beans

Eksamen i Internetteknologi Fagkode: ITE1526

Enkle generiske klasser i Java

Klasser skal lages slik at de i minst mulig grad er avhengig av at klienten gjør bestemte ting STOL ALDRI PÅ KLIENTEN!

1. SQL datadefinisjon og manipulering

NB!!! Veldig korte svar er gitt her. Disse burde det vært skrevet mer på ved en eksamen..

INF Uke 10. Ukesoppgaver oktober 2012

Å bruke Java API-et til å sortere tabeller/arraylister der elementene er (referanser til) objekter

2 Om statiske variable/konstanter og statiske metoder.

Løsningsforslag ukeoppg. 9: okt (INF Høst 2011)

Løsningsforslag ukeoppg. 6: 28. sep - 4. okt (INF Høst 2011)

Transaksjoner og flerbrukerproblematikk. Transaksjoner

Dagens tema Kapittel 8: Objekter og klasser

9. ASP med databasekopling, del II

HØGSKOLEN I SØR-TRØNDELAG

HØGSKOLEN I SØR-TRØNDELAG

Spesifikasjon av Lag emne

Hva er Derby og Java DB? Denne forelesningen. Java Database Connectivity (JDBC) Hva er Derby og Java DB?

Datamodellering 101 En tenkt høgskoledatabase

INF1000 HashMap. Marit Nybakken 2. november 2003

Ansvarsdrevet OO: CRC og UML Sekvensdiagrammer

8. JDBC-programmering med tilrettelegging for webapplikasjoner

Algoritmer og datastrukturer Kapittel 3 - Delkapittel 3.1

HØGSKOLEN I SØR-TRØNDELAG

INF1000: Forelesning 7

INF1000 Metoder. Marit Nybakken 16. februar 2004

INF1000: Forelesning 7. Konstruktører Static

Tilkobling og Triggere

BOKMÅL Side 1 av 7. KONTINUASJONSEKSAMEN I FAG TDT4100 Objektorientert programmering / IT1104 Programmering, videregående kurs

Java Database Connectivity (JDBC) Norvald H. Ryeng

IN1010 våren Repetisjon av tråder. 15. mai 2018

Løsningsforslag Test 2

Array&ArrayList Lagring Liste Klasseparametre Arrayliste Testing Lenkelister

TDT4102 Prosedyre og Objektorientert programmering Vår 2014

public class NaivRiking { private HeldigSnylter minsnylter; public NaivRiking(HeldigSnylter h) { minsnylter = h;

Hva er verdien til variabelen j etter at følgende kode er utført? int i, j; i = 5; j = 10; while ( i < j ) { i = i + 2; j = j - 1; }

INF Uke 10. Løsningsforslag ukesoppgaver oktober 2012

programeksempel Et større En større problemstilling Plan for forelesingen Problemstillingen (en tekstfil) inneholdt ordet "TGA"

Introduksjon til fagfeltet

INF1010 Arv. Marit Nybakken 2. februar 2004

Eksekveringsrekkefølgen (del 1) Oppgave 1. Eksekveringsrekkefølgen (del 2) Kommentar til oppgave 1. } // class Bolighus

(MVC - Model, View, Control)

UNIVERSITETET I OSLO

class Book { String title; } class Dictionary extends Book { int wordcount; } class CartoonAlbum extends Book { int stripcount; }

1- og 2-veis Innkapsling Java Stabel Kø Prio-kø Iterator. Enveis- og toveislister Innkapsling («boxing») (Big Java 6.8.5)

CORBA Component Model (CCM)

INF1010, 15. januar time. Parametriserte klasser (generiske klasser) Stein Gjessing Inst. for Informatikk Universitetet i Oslo

UNIVERSITETET I OSLO

Array&ArrayList Lagring Liste Klasseparametre Arrayliste Testing Lenkelister Videre

PG4200 Algoritmer og datastrukturer Lab 1. 8.januar I dag skal vi undersøke en rekke velkjente databeholdere i Java:

Å programmere databasetjeneren JavaDB. Programkoden ligger i databasen

Arv. Book book1 = new Book(); book1. title = "Sofies verden" class Book { String title; } class Dictiona ry extends Book {

Applikasjonsutvikling med databaser

1. Relasjonsmodellen Kommentarer til læreboka

IN1010 våren januar. Objektorientering i Java

Eksamen. Objektorientert Programmering IGR 1372

UNIVERSITETET I OSLO

IN våren 2018 Tirsdag 16. januar

1- og 2-veis Innkapsling Java Stabel Kø Prio-kø Iterator. Enveis- og toveislister Innkapsling («boxing») (Big Java 6.8.5)

JSP - 2. Fra sist. Hvordan fungerer web? Tjenerside script HTML. Installasjon av Web-tjener Et enkelt JSP-script. Ønsker dynamiske nettsider:

Transaksjoner og flerbrukerproblematikk. Transaksjoner

JavaScriptbibliotek. Introduksjon MVVC. Informasjonsteknologi 2. Gløer Olav Langslet Sandvika VGS

1. NetBeans IDE: Lage en enkel mobilapplikasjon

TDT4100 Objektorientert programmering

10. ASP og SQL Innledning Recordset-objektet. Innhold. Referanse til læreboka Kapittel Se detaljer nedenfor.

INF1000 Prøveeksamen Oppgave 7 og 9

J2EE og distribuerte systemer Leksjon 8: Oppbygning av J2EE-applikasjoner

Algoritmer og datastrukturer E Løkker i Java

Oppgaver Oppgave a: Sett opp mulige relasjoner

INF1010 Tråder II 6. april 2016

En lett innføring i foreninger (JOINs) i SQL

UNIVERSITETET I OSLO

Stein Gjessing, Institutt for informatikk, Universitetet i Oslo

IN våren 2019 Onsdag 16. januar

Liste som abstrakt konsept/datatype

Fra krav til objektdesign

Oppgave 1 (Opprett en database og en tabell)

notater Gule lapper Mine Et praktisk eksempel med objekter IT2 Læreplansmål Gløer Olav Langslet Sandvika VGS

INF1010 Eksamenstips. Løsningsforslag prøveeksamen del 1.

INF våren 2017

6108 Programmering i Java. Leksjon 5. Tabeller. Roy M. Istad 2015

Oblig 4Hybelhus litt mer tips enn i oppgaven

Oblig 4 (av 4) INF1000, høsten 2012 Værdata, leveres innen 9. nov. kl

Forklaring til programmet AbstraktKontoTest.java med tilhørende filer Konto.java, KredittKonto.java, SpareKonto.java

ORDBMS og OODBMS i praksis

Datamodellering og databaser SQL, del 2

. Ved sensur vl1 ahe bokstaverte deloppgaver (a, b, c,...) telle like mye.

Introduksjon til objektorientert. programmering. Hva skjedde ~1967? Lokale (og globale) helter. Grunnkurs i objektorientert.

1. SQL spørringer mot flere tabeller

OBJEKTER SOM EN PROGRAMMERINGS-TEKNIKK

Spesifikasjon av Lag emne. Kursregistrering bruksmønstermodell (ny versjon) Dagens forelesning. Fra krav til objektdesign

Sortering med Comparable og Comparator

Transkript:

J2EE og distribuerte systemer Leksjon 10: Entity Beans (BMP) Leksjonen er forfatters eiendom. Som kursdeltaker kan du fritt bruke leksjonen til eget personlige bruk. Kursdeltakere som ønsker å bruke leksjonene f.eks. til undervisning eller kursformål må ta direkte kontakt med forfatter for nærmere avtale. Copyright: Tomas Holt/TISIP Publisert 23.10.03. Frist innlevering av øvingsoppgaver 10.11.03 Innhold 1 KOMMENTAR TIL LEKSJONEN... 1 2 LAGRING AV VARIG DATA... 1 3 ENTITY BEANS... 3 4 BMP ENTITY BEAN... 9 4.1 ejbcreate()... 10 4.2 Finnmetoder (finder methods)... 10 4.3 setentitycontext()... 10 4.4 ejbactivate() og ejbpassivate()... 11 4.5 home-metoder... 11 4.6 Synkroniseringsmetodene ejbload() og ejbstore()... 11 5 EKSEMPEL PÅ EN ENTITY BEAN (BMP)...12 6 DEPLOYTOOL OG ENTITY BEANS...19 7 OPPGAVE...22

1 Kommentar til leksjonen Leksjonen omhandler kapittel 11 og 12 i boka. Assosiasjoner kap. 11.3 er foreløpig ikke viktig. Alt som står om CMP (Container Managed Persistence) 1.1 vil ikke være pensum (bare CMP 2.0). I stedet for eksemplet i kapittel 12 kan dere se på eksemplet i leksjon 10. I leksjonen blir det gjennomgått en del som har med transaksjoner å gjøre. Dette er tatt med kun som motivasjon for hvorfor bruke Entity Beans. Dette er ikke viktig i denne leksjonen. Vi skal komme tilbake til transaksjoner i leksjon12. Foreløpig kan du derfor bare skumme gjennom det som har med transaksjoner å gjøre. 2 Lagring av varig data Du vet nå hvordan Session Beans brukes. Veldig ofte vil man i tjenersystemer lagre data permanent. I de fleste tilfellene brukes en database. Et eksempel kan være en applikasjon for å overføre penger mellom kontoer. En konto vil da typisk være en rad i en databasetabell. Denne raden vil da inneholde kontonummer og saldo. I tillegg så vil kontonummeret være knyttet opp mot en person (eier av kontoen). Spørsmålet er hvordan lager man en J2EE-applikasjon for overføring av penger mellom disse kontoene. Du kan selvsagt lage en Session Bean som sørger for denne overføringen. Dette kan bli omtrent slik: public class KontoOverføringBean extends GeneriskSessionBean{... public void overfør(int frakonto, int tilkonto, int beløp){ //hent oppkobling til databasen //trekk beløp fra frakonto i databasen med en SQL-setning //sett inn beløp på tilkonto i databasen med en SQL-setning... Dette er vel greit? Er det noen grunn til å gjøre ting annerledes? La oss først prøve en objektorientert tilnærming til problemet. Det vi ønsker å gjøre her er å overføre penger mellom to kontoer, men hvor er kontoobjektene? Vi har ingen. Løsningen strider altså med den objektorienterte tankegangen som java bygger på. Hvis vi ikke hadde brukt en database her så ville vi ha valgt å lage en egen klasse Konto. KontoOverføringBean kunne da brukt objekter av klassen Konto, i stedet for å bruke databasen direkte. I dette tilfellet vil det være opp til Konto-klassen hvordan man velger å lagre dataene. Løsningen kan nå se slik ut: public class KontoOverføringBean extends GeneriskSessionBean{... public void overfør(int frakonto, int tilkonto, int beløp){ Konto kontout = //finn kontoen frakonto Konto kontoinn = //finn kontoen tilkonto kontout.taut(beløp); //trekker beløp fra denne kontoen kontoinn.settinn(beløp); //setter inn beløp på kontoen... I koden over forutsetter jeg at klassen Konto har metodene taut() og settinn(). Ettersom vi må lagre dataene i Konto-klassen (saldo) til et varig medium, vil det være naturlig å bruke en 1

database. I J2EE har vi faktisk en egen type EJB til dette formålet, nemlig Entity Beans. Tenk deg at Konto-klassen blir implementert som en Entity Bean (tenk foreløpig på dette som en vanlig java-klasse, vi kommer tilbake til hva en Entity Bean er). Bør Konto EJB en inneholde databasekode? Problemet med en slik løsning er at vi vil knytte EJB en mot et bestemt databasesystem. En av grunnene til å bruke EJB var nettopp for å unngå dette. Det er mulig å omgå problemet med å putte all databasekommunikasjon i en egen databaseklasse, som EJB en bruker. Vi kan da bare endre databaseklassen ved skifte av databasesystem. Når man skiller ut databasekoden/lagringskoden i en egen klasse kalles dette DAO (Data Access Object). Situasjonen er imidlertid ennå ikke optimal. For å illustrere dette, kan du tenke deg at den kontoen vi skal hente penger fra, ligger i DnB sin database. Pengene skal overføres til en kunde i SpareBank1 som har sin egen database. Ettersom vi her skal overføre pengene fra et databasesystem til et annet, kan vi ikke bruke databasesystemenes transaksjonshåndtering (som vi har gjort så langt), fordi hvert databasesystem kun vet hva som foregår internt. Koden for overføringen er slik som vist i metoden overfør() over (koden er kanskje nå flyttet til en DAO-klasse, men det spiller ingen rolle for eksemplet her). Beløpet som skal overføres er 100 kroner. Beløpet trekkes først fra kontoen i DnB. I det pengene skal til å settes inn på kontoen i SpareBank1 så viser det seg at denne kontoen er slettet. Hva er så resultatet av dette? Jo, vi har faktisk trukket 100 kroner fra en konto, uten at pengene har blitt overført dit de skulle! Dette er en helt uholdbar situasjon. Det er mulig å løse dette med å føre pengene tilbake igjen, men dette krever at du i koden din faktisk tar høyde for at problemet kan oppstå! Poenget er at pengene skal ikke trekkes før man vet at overføringen går i orden. For å unngå problemet er vi avhengig at hele overføringen opptrer som en transaksjon. Enten skal alt utføres eller så skal ingenting utføres. Tenk deg at vi har et objekt som identifiserer kontoen i Dnb og et objekt som identifiserer kontoen i Sparebank1. Disse objektene er laget slik, at når endringer skjer på objektene, så vil også databasen oppdateres. Objektene er imidlertid laget slik at hvis de er med i en transaksjon, så vil de ikke oppdatere databasen før de får eksplisitt beskjed om det. Det er applikasjonstjeneren sin oppgave å gi beskjed når oppdateringen skal utføres. Koden kan nå se slik ut: public void overfør(int frakonto, int tilkonto, beløp){ //start transaksjon, alt som kommer etter ligger i en transaksjon //finn objektet for frakonto //finn objektet 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() Scenariet over kalles to-fase-commit. Applikasjonstjeneren fungerer her som sjef i transaksjonen og kalles da for en monitor. Objektene må hele tiden forholde seg til monitoren og kan ikke gjøre commit før de har fått beskjed fra monitoren. Det er hele tiden monitoren sin oppgave å sjekke om alle involverte i transaksjonen kan gjøre sin oppgave. Hvis en av deltakeren (objektene) ikke kan gjøre sin del, gis beskjed til alle de andre deltakerne om å utføre rollback (dvs. endringene skal ikke gjøres varige). Du vil senere se at du selv kan spesifisere at en metode skal være med i en transaksjon. Applikasjonstjeneren (monitoren) vil da sørge for at alt inne i metoden blir med i denne transaksjonen. Det er også fullt mulig å spesifisere at metoden ikke skal være med i en transaksjon. 2

Jeg håper nå at du har fått litt motivasjon for å bruke EJB er til å representere databaseinnhold (evt. annen varig datalagring). I J2EE gjøres dette ved å bruke Entity Beans. En slik Entity Bean vil da typisk representere en rad i en databasetabell, og alle endringer i denne EJB en vil føre til endringer i den bakenforliggende databasen. Entity Beans representerer altså som du nå skjønner data. Hva representerer så Session Beans? Session Beans representerer prosesser. Det kan f.eks. være kalkulering av hvor mange dollar 50 norske kroner er verdt. Her er det altså en beregning/prosessering som skjer. Når du lager en J2EE-applikasjon vil du derfor sørge for at prosessering ligger i Session Beans, mens varige data representeres gjennom Entity Beans. Et eksempel kan være en bedrift som har mange bøker. Bøkene vil da typisk være representert av Entity Bean instanser. Hvis denne bedriften lever av å selge bøker, vil det da typisk være en Session Bean som sørger for å behandle/prosessere forespørsler fra kundene. En slik forespørsel kan for eksempel være å se alle bøkene som er tilgjengelig for salg. I dette tilfellet vil en Session Bean returnere Entity Bean instansene som representerer bøkene (eller en kopi av innholdet i disse). La oss se hva som skal til for å lage en Entity Bean. 3 Entity Beans Entity Beans er ikke veldig forskjellig fra Session Beans. På samme måte som for Session Beans, vil vi lage to eller fire interface, navngivingen blir den samme. I motsetning til Session Beans vil vi ofte bruke lokale interface til Entity Beans. Grunnen er at det som regel vil ligge en Session Bean foran. Klienten får kun kontakt via en Session Bean. For å gjøre kommunikasjonen mellom EJB ene mest mulig effektiv vil det derfor være ønskelig å bruke lokale interface (lokale referanser), i stedet for å kommunisere via nettverksstakken som gir ekstra overhead. 3

Home-interface navneks. KontoHome/ KontoLocalHome home-objekt Konto create(integer kontonr) throws. //evt. flere create()-metoder Konto findbyprimarykey(integer kontonr) throws.. Collection findbyetternavnnavn(string navn) throws. //metoder for å endre data void settinn(double beløp) void taut(double beløp) EJB-objekt Remote/Local-interface navneks. Konto/ KontoLocal Figur 1: Interface og tilhørende objekter Av figuren over kan du se at interfacene for en Entity Bean er som for en Session Bean. Du kan imidlertid se at det ligger flere metoder i Home-interfacet. Når home-objektet opprettes ved utlegging av applikasjonen, vil metodene i Home-interfacet være tilgjengelig for klienter. EJB-objektet vil først opprettes som følge av et kall til en av metodene i home-objektet. Du kan se at vi kan få tak i en referanse til et EJB-objekt gjennom andre metoder (f.eks. findbyprimarykey()) enn create(). En av grunnene til at vi ikke ønsker klienten til å kommunisere direkte med en Entity Bean kan være behovet for transaksjoner. Som vist i forrige kapittel var vi avhengig av at overføringen mellom to kontoer ble utført som en transaksjon. La oss se på overføring mellom to kontoer hvor vi bruker en EJB for å representere hver konto. 4

EJB Klient Konto: 1 Saldo: 1000 (900) taut(100); settinn(100); EJB Konto 2 Saldo: 100 (200) Database Figur 2: Overføring mellom kontoer via EJB er Hvordan skal vi håndtere transaksjonen i figuren over. Vi vet jo at metodene taut() og settinn() må utføres som en transaksjon. Tidligere har vi latt databasesystemet sørge for transaksjonshåndtering, men problemet nå er at hver EJB-instans (konto 1 og konto 2) har sin egen oppkobling til databasen. Dette gjør at vi heller ikke kan støtte oss til databasesystemets transaksjonshåndtering fordi denne transaksjonshånderingen er pr. oppkobling. Mao. så vil det trekkes 100 kroner i en transaksjon og så vil det settes inn 100 kroner i en annen transaksjon. Måten å unngå dette på er at EJB-instansene ikke oppdaterer databasen før de er sikre på at den andre EJB-instansen også kan oppdatere databasen. Dette gjøres via to-fase-commit. For å få dette til å fungere trenger vi en sjef for transaksjonen. Dette vil være applikasjonstjeneren. I.o.m at klienten ikke ligger inn i applikasjonstjeneren må vi flytte ansvaret for transaksjonen fra klienten. Det naturlige her vil være å bruke en Session Bean, se figuren under. 5

Container EJB Klient Session EJB Konto: 1 Saldo: 1000 (900) overfør(1,2,100) taut(100) settinn(100) EJB Database Konto 2 Saldo: 100 (200) Figur 3: Fasade av Session Beans En metode i en Entity EJB vil bestandig være en transaksjonsenhet eller en del av en (containeren vil alltid ha ansvaret for dette). I en Session Bean kan vi spesifisere at en metode skal utføres som en transaksjonsenhet. Det vil nå være containeren sin oppgave overvåke transaksjonen. Merk at at taut() og settinn() nå ligger i metoden overfør(). Denne metoden er nå spesifisert som en transaksjonsenhet. Det gjør at kallene til taut() og settinn() ikke direkte vil føre til endringer i databasen. Det skjer først når begge EJB-instansene (konto 1 og 2) har gitt beskjed tilbake til containeren om at de er klare til å kjøre commit. Hvis en av EJB ene gir beskjed om at dette ikke går (unntak), så vil transaksjonen rulles tilbake (EJB ene vil ikke lagre endringene). Det er mulig å selv ta ansvaret for at en metode eller deler av en metode i en Session Bean skal utføres som en transaksjonsenhet. I dette kurset vil vi bestandig la containeren ta dette ansvaret (i de tilfellene vi trenger transaksjoner). Du kan spesifisere hvilken strategi du vil følge ved å bruke deploytool. Velg EJB-implementasjonsklassen i deploytool og så skillearket for Transactions. Her har du valgene Bean Managed og Container Managed. Ved å velge Container Managed vil du få opp en oversikt over alle metodene i EJB en, og du kan bestemme hvilken transaksjonshåndtering du ønsker. Foreløpig nøyer vi oss med å si at du kan bruke standardvalget required i de fleste tilfeller. 6

Figur 4: Metoder og transaksjoner Vi kommer tilbake til transaksjoner i leksjon 12. Deploytool har en tendens til å klage på at transaksjonshåndtering ikke er spesifisert for en spesiell metode. Hvis du får dette problemet kan du endre typen transaksjon for metoden (til noe annet en required), trykk deploy, for så å endre tilbake til required. En annen grunn til at vi ikke vil la klienten kommunisere direkte med en Entity Bean er at det ofte blir veldig mye nettverkstrafikk av det. Tenk bare på en klient som skal gjøre endringer i 100 instanser av en Entity Bean, dette vil føre til nettverkstrafikk mellom klienten og alle de 100 EJB-instansene. La i stedet klienten spesifisere hvilke instanser som skal oppdateres. Beskjed sendes fra klienten til en Session Bean som gjør de nødvendige kallene. Dette gjør at vi slipper unna med en melding over nettverket (se figurene under)! 7

fjernaksess Bok 1 endrepris() Bok 2 endrepris() Klient endrepris() Bok 100 Figur 5: 100 kall til 100 Entity Beans fjernaksess Bok 1 Bok 2 endrepris() Klient Endre pris på alle bøker + 10 % Session Bean endrepris() Bok 100 Figur 6: 1 kall til en Session Bean Den anbefalte måten å designe J2EE-applikasjoner på er altså å bruke Session Beans mellom klient og Entity Beans. Man kaller dette en fasade av Session Beans. Det finnes to ulike typer Entity Beans. Den ene kalles BMP (Bean Managed Persistency). I dette ligger at du som programmerer selv må skrive koden som sørger for å lagre innholdet (dataene) til et varig medium (typisk database). Den andre varianten heter CMP (Container 8

Managed Persistency). Her vil du ikke selv skrive noe kode for lagring av dataene. I dette tilfellet vil containeren sørge for at dataene i EJB en blir lagret ved behov. Begge metodene har sine styrker og sine svakheter. Styrken til BMP er at du har kontroll! Ulempen er selvsagt at du må programmere mer, og ikke minst gjøre ting riktig. I tillegg så vil du fort kunne gjøre lagringen knyttet mot et lagringssystem. Du programmerer f.eks. en EJB for å fungere mot Cloudscape, men noen andre vil bruke Oracle. Ved å bruke CMP vil dette aldri bli noe problem. Styrken til CMP er at man slipper å skrive kode for å lagre dataene til et varig medium. I.o.m at dette er containeren sin oppgave er det heller ikke (i utgangspunktet) noe problem å skifte lagringsmedium. Det er da opp til applikasjonstjeneren å gjøre nødvendige endringer. Dette kan høres fantastisk og usannsynlig ut, men vent til neste leksjon så skal vi se på hvordan dette er mulig. Teoretisk gir bruk av CMP også bedre ytelse, fordi containeren kan gjøre optimaliseringer som ikke er mulig med BMP. Jeg har allerede beskrevet at en Entity EJB typisk vil representere en rad i en database. La oss se på en database som inneholder en tabell med kontoer. Konto tabellen kan f.eks. ha følgende 4 felter. Kontonummer. Dette er et unikt nummer for en konto, og vil derfor typisk være den primære nøkkelen til denne tabellen. Fornavn. Dette er et felt som beskriver fornavnet til eieren av kontoen. Etternavn. Dette er et felt som beskriver etternavnet til eieren av kontoen. Saldo. Beskriver saldoen. Merk at tabellen under normalt ikke vil lages slik. Man vil skille ut navnene i en egen tabell, fordi man lagrer navnet til en person flere ganger (Ola Normann). I dette eksemplet ser vi imidlertid bort i fra problemet (vi skal komme tilbake til det i en senere leksjon). Kontonr Fornavn Etternavn Saldo 140027000 Ola Normann 100 140027001 Ola Normann 200 140027002 Kari Normann 300 Poenget med å vise dette er at hver rad er unikt bestemt av primærnøkkelen (kontonr). Når en Entity Bean instans skal representere en konto fra tabellen må den altså inneholde verdiene for kontonr, fornavn, etternavn og saldo. Hvordan finner vi så riktig Entity Bean instans når vi skal endre en bok? Det naturlige vil være å bruke det som er unikt for akkurat den boken det er snakk om, nemlig kontonr. En Entity Bean instans vil derfor typisk kunne identifiseres gjennom primærnøkkelen til raden i databasetabellen. 4 BMP Entity Bean I denne leksjonen konsentrerer vi oss om BMP Entity Beans. Vi kan først starte med å se på hvilke metoder som trengs i en slik EJB-implementasjonsklasse. Beskrivelsen under kan nok være noe tørr/uoversiktlig ved første gangs lesing, og må sees i sammenheng med neste kapittel som viser metodene i praksis. 9

4.1 ejbcreate() Denne metoden må faktisk ikke være med i en Entity Bean. Du kan imidlertid implementere flere ejbcreate()-metoder så lenge de har ulike argumenter. La meg først konstantere at denne metoden faktisk oppretter data. Når du kaller create()-metoden på EJB-objektet vil det vanligvis bli opprettet en ny rad i en databasetabell. Dette er naturlig da en Entity Bean representerer data og må lagres i et varig medium. Dette kan virke litt merkelig da vi tidligere (Session Beans) har bruker create()-metoden til å få tak i en referanse til EJB-objektet. Hvordan kan vi få tak i en referanse til EJB-objektet uten å kalle create() på home-objektet? Saken er at vi kan bruke finnmetoder (se lenger ned) i stedet. Vi kan jo ikke opprette en ny rad i databasetabellen hver gang vi vil bruke en Entity Bean. I motsetning til Session Beans så vil ejbcreate()-metoden returnere en verdi! Verdien som returneres fra denne metoden må være primærnøkkelen til objektet. Metoden vil typisk sørge for å opprette en ny rad i en databasetabell og returnere primærnøkkelen til denne raden. Metoden skal hive CreateException ved feil. 4.2 Finnmetoder (finder methods) En Entity Bean skal ha minst en finnmetode. Hvordan kan en Entity Bean finnes på en unik måte? Jo, på primærnøkkelen. Det skal derfor være en metode som heter ejbfindbyprimarykey() throws FinderException. Denne metoden tar alltid den primære nøkkelen som argument og returnerer den primære nøkkelen som returargument. Det blir containeren (gjennom home-objektet) sin oppgave å returnere referansen til et EJB-objekt. Dette gjøres automatisk. ObjectNotFoundException (arver fra FinderException) hives hvis det ikke finnes data for den valgte primærnøkkelen. La oss gå tilbake til det tilfellet hvor vi har en Entity Bean som representerer kontoer. Det kan i dette tilfellet være aktuelt å finne kontoene til personer som har etternavnet Normann. For å gjøre dette lager vi metoden ejbfindbyetternavn() (findbyetternavn() i Home-interfacet). Inne i denne metoden returneres en samling (Collection) med primærnøklene. Også i dette tilfellet vil det skje en omforming, slik at det som returneres til klienten er referanser til EJB-objekter. I tilfellet det skal returneres en Collection og man ikke finner noen objekter, returneres en tom Collection. 4.3 setentitycontext() Metoden kalles kun en gang på en EJB-implementasjonsobjekt. Merk at ejbcreate() kan kalles flere ganger, da det kun opprettes en ny rad i en tabell. Merk at et EJBimplementasjonsobjekt godt kan representere en rad i en tabell i et øyeblikk, og i neste øyeblikk representere enn annen rad. Dette er mulig fordi alle data lagres i et varig medium, så man mister ikke noen data i dette tilfellet. For å finne ut hvilken primærnøkkel implementasjonsobjektet faktisk representerer må man kalle metoden getprimarykey() på EntityContext-objektet. Hvert EJB-implementasjonsobjekt får et slikt objekt som parameter når setentitycontext() blir kalt. Metoden ejbactivate() blir kalt når containeren har gitt EJB-implementasjonsobjektet en ny primærnøkkel. 10

4.4 ejbactivate() og ejbpassivate() Disse metodene kalles av containeren ved skifte av primærnøkkel. EJBimplementasjonsobjektet vil altså i dette tilfellet få nytt innhold, eller med andre ord, typisk representere en annen rad i en databasetabell. 4.5 home-metoder Dette er metoder som kan sammenliknes med klassemetoder. Vi kan f.eks. ha en metode som heter gettotaltantallkontoer(). Det sier seg selv at dette ikke er en metode du typisk kaller på et konto-objekt (snakker her om en EJB-implementasjonsobjekt), da dette objektet kun holder styr på seg selv. Løsningen ligger i å legge metoden i home-objektet. Ved å spesifisere metoden gettotaltantallkontoer() i både EJB-implementasjonsklassen og i Home-interfacet vil dermed metoden kunne returnere antall bøker som finnes. Vi kan derfor finne antall bøker uten å finne et EJB-objekt først. 4.6 Synkroniseringsmetodene ejbload() og ejbstore() I Entity Beans (BMP) har vi to metoder som er veldig viktige. Dette er metodene som sørger for å synkronisere dataene i EJB en med databasen (e.l). Det er viktig å være klar over at dataene i EJB en alltid er up-to-date. Du får aldri gamle utdaterte data ved å gå igjennom en Entity Bean. Synkronisering skjer med metodene ejbload() og ejbstore(). Når en EJBimplementasjonsobjekt skal gjøre noe på sine variabler så vil de bestandig bli lastet inn fra databasen, via ejbload(). Ved ferdig oppdatering så vil de bli skrevet tilbake til databasen, via ejbstore(). Under vises en figur av hvordan et kall til metoden taut() i en konto EJB vil være. Det samme gjelder uansett hvilken metode (forretningslogikkmetode) som kalles på en Entity EJB. Klient EJB-objekt EJBimplementasjonsobjekt 1: taut() 2: ejbload() 3: taut() 4: ejbstore() Når en klient vil ta ut penger så kalles metoden taut() på EJB-objektet. Dette fører til at containeren (gjerne via EJB-objektet) sørger for å kalle metoden ejbload() på EJBimplementasjonsobjektet. Dette fører til at variablene i dette objektet (f.eks. saldo) blir lik verdien som ligger i databasen. Så kan metoden taut() kalles (nå vil variablene være 11

oppdaterte). Når metoden er ferdig må vi sørge for å lagre dataene tilbake i databasen. Dette sørger containeren for via et kall til ejbstore(). 5 Eksempel på en Entity Bean (BMP) La oss se på et eksempel på en Entity Bean. Dette eksemplet er en konto, hvor kontoen representeres med en Entity Bean. Merk at eksemplet kan gi trøbbel når det gjelder isolering mellom transaksjoner. Dette kommer vi tilbake til i leksjon 12. Ikke bry deg med problemet foreløpig (det er en enkel løsning på problemet). Koden til eksemplet finner du også vedlagt leksjonen. Metoder som skal endre innholdet, eller gjøre noe på innholdet i en Entity Bean, legger vi som tidligere i interfacene til EJB en. Disse er de samme som før. Vanligvis vil vi bruke lokale-interface for en Entity Bean. For å gjøre dette eksemplet enklest mulig har jeg imidlertid valgt å bruke fjern-interface. Dette gjør at vi kan bruke en ekstern klient direkte mot EJB en, noe som gjør testing enklere. Det kan derfor være en ide å bruke fjern-interface til testing av EJB en, og gå over til lokale-interface når applikasjonen er ferdig. import javax.ejb.*; import java.rmi.*; public interface KontoBMP extends EJBObject{ public void settinn(int kroner) throws RemoteException; public void taut(int kroner) throws RemoteException, KontoException; public int hentsaldo() throws RemoteException; //merk at det ikke er noen metode for å hente primærnøkkelen kontonr //denne metoden arves fra EJBObject og er derfor bestandig tilgjengelig! Vi ser at EJB en vår har tre metoder som klienten kan bruke: settinn() taut() hentsaldo() I tillegg til disse metodene vil det også være flere metoder som er tilgjengelig for klienten. Vi har tidligere vært inne på at Remote-interfacet (evt. Local-interfacet) danner grunnlaget for EJB-objektet. EJB-objektet inneholder metodene fra interfacet, men også metoder fra EJBObject som interfacet arver fra. EJBObject inneholder blant annet metodene remove() getprimarykey() Når det gjelder Home-interfacene (Home/HomeLocal) så er det for Entity Beans ofte flere metoder enn tilfellet for Session Beans. import javax.ejb.*; import java.rmi.*; import java.util.*; public interface KontoBMPHome extends EJBHome{ public KontoBMP create(integer nyttkontonr, String fornavn, String etternavn) throws RemoteException, CreateException; //finn metoder public KontoBMP findbyprimarykey(integer kontonr) throws RemoteException, FinderException; 12

public Collection findbysaldooverbeløp(int saldo) throws RemoteException, FinderException; public Collection findallekontoer() throws RemoteException, FinderException; //home-metoder public int finntotaltantallkontoer() throws RemoteException; //home-metode Entity Beans skiller seg fra Session Bean blant annet ved at de har finnmetoder. Du kan se fra Home-interfacet at EJB en har finnmetodene findbyprimarykey() findbysaldooverbeløp() findallekontoer() I implementasjonsklassen finner du disse metodene som ejbfindbyprimarykey() ejbfindbysaldooverbeløp() ejbfindallekontoer() Dette er navnekonvensjonen. Metoden findbyprimarykey() og ejbfindbyprimarykey() må være med, resten er opp til oss selv. Som du ser har jeg laget en metode for å finne alle kontoer som har saldo større en et visst beløp. Finnmetodene returnerer alltid referanser til et eller flere EJB-objekter (i sistnevnte tilfelle pakket i en Collection). I noen tilfeller trenger vi andre returverdier, men uten at metoden knyttes direkte til en EJB-instans. Vi kan da bruke home-metoder, og finntotaltantallkontoer() er en slik metode. Du finner igjen denne metoden som ejbhomefinntotaltantallkontoer() i implementasjonsklassen. Merk at all databasekode er lagt inne i EJB-implementasjonsklassen. En bedre løsning vil være å legge dette i en egen klasse (DAO), men jeg har valgt å gjøre det slik da vi kan sammenlikne BMP-koden, med CMP-kode (kommer i neste leksjon). import javax.ejb.*; import java.util.*; import javax.sql.*; import javax.naming.*; import java.sql.*; public class KontoBMPBean implements EntityBean{ private Integer kontonr; // denne variablen trengs ikke. Vi kan i stedet bruke kontekst.getpriamrykey(). private String fornavn; private String etternavn; private int saldo; private EntityContext kontekst; DataSource ds; //metoden oppretter en rad i DB-tabellen public Integer ejbcreate(integer kontonr, String fornavn, String etternavn) throws CreateException{ log("ejbcreate()"); String sql = "insert into konto values(?,?,?,?)"; Connection forbindelse = null; PreparedStatement prep = null; if (fornavn.equals("")) throw new CreateException("Eier av konto må ha et fornavn"); if (etternavn.equals("")) throw new CreateException("Eier av konto må ha et etternavn"); 13

//setter variablene bortsett fra primærnøkkelen som overlates til container this.fornavn = fornavn; this.etternavn = etternavn; this.saldo = saldo; //lagrer dataene i db try{ forbindelse = ds.getconnection(); prep = forbindelse.preparestatement(sql); prep.setint(1,kontonr.intvalue()); prep.setstring(2,fornavn); prep.setstring(3,etternavn); prep.setint(4,0); prep.execute(); catch(sqlexception e){ log("unntak " + e); throw new CreateException("Problemer med å legge til data i DB" + e.getmessage()); finally{ lukkforbindelser(null, prep, forbindelse); return kontonr; /**************** finnmetoder *********************************/ //metoden brukes for å finne en EJB. Returnerer kun primærnøkkelen til EJB'en //containeren sørger for å returnere referansen til et EJB-objekt! public Integer ejbfindbyprimarykey(integer kontonr) throws FinderException{ log("findbypriamrykey"); String sql = "select kontonr from konto where kontonr=?"; ResultSet res = null; Connection forbindelse = null; PreparedStatement prep = null; try{ forbindelse = ds.getconnection(); prep = forbindelse.preparestatement(sql); prep.setint(1,kontonr.intvalue()); res = prep.executequery(); if (!res.next()) throw new ObjectNotFoundException("Finner ikke kontonr " + kontonr.intvalue()); //søker etter en nøkkel som ikke finnes catch(sqlexception e){ log("unntak " + e); throw new EJBException(e); //system-feil vi ikke kan rette på finally{ lukkforbindelser(res,prep,forbindelse); return kontonr; //finner alle kontoer, returnerer tom liste hvis ingen finnes //metoden returnerer kun en Collection med primærnøkler //container sørger for å returnere EJB-objektene public Collection ejbfindallekontoer() throws FinderException{ log("findallekontoer"); String sql = "select kontonr from konto"; ArrayList kontoer = new ArrayList(); Connection forbindelse = null; PreparedStatement prep = null; ResultSet res = null; try{ 14

forbindelse = ds.getconnection(); prep = forbindelse.preparestatement(sql); res = prep.executequery(); while (res.next()){ int tmpnøkkel = res.getint("kontonr"); kontoer.add(new Integer(tmpNøkkel)); catch(sqlexception e){ log("unntak " + e); throw new EJBException(e); finally{ lukkforbindelser(res,prep,forbindelse); return kontoer; //finner alle kontoer for etternavn, returnerer tom liste hvis ingen finnes public Collection ejbfindbyetternavn(string navn) throws FinderException{ log("findbyetternavn"); String sql = "select kontonr from konto where etternavn=?"; ArrayList kontoer = new ArrayList(); PreparedStatement prep = null; Connection forbindelse = null; ResultSet res = null; try{ forbindelse = ds.getconnection(); prep = forbindelse.preparestatement(sql); prep.setstring(1,navn); res = prep.executequery(); while (res.next()){ int tmpnøkkel = res.getint("kontonr"); kontoer.add(new Integer(tmpNøkkel)); catch(sqlexception e){ log("unntak " + e); throw new EJBException(e); finally{ lukkforbindelser(res,prep,forbindelse); return kontoer; //returnerer alle kontoer som har saldo over et gitt beløp public Collection ejbfindbysaldooverbeløp(int saldo) throws FinderException{ log("findbyetternavn"); String sql = "select kontonr from konto where saldo >?"; ArrayList kontoer = new ArrayList(); PreparedStatement prep = null; Connection forbindelse = null; ResultSet res = null; try{ forbindelse = ds.getconnection(); prep = forbindelse.preparestatement(sql); prep.setint(1,saldo); res = prep.executequery(); while (res.next()){ int tmpnøkkel = res.getint("kontonr"); kontoer.add(new Integer(tmpNøkkel)); 15

catch(sqlexception e){ log("unntak " + e); throw new EJBException(e); finally{ lukkforbindelser(res,prep,forbindelse); return kontoer; /*********************** home-metoder **************************/ //finner totalt antall kontoer public int ejbhomefinntotaltantallkontoer(){ log("gettotaltantallkontoer"); int antall=0; String sql = "select kontonr from konto"; PreparedStatement prep = null; Connection forbindelse = null; ResultSet res = null; try{ forbindelse = ds.getconnection(); prep = forbindelse.preparestatement(sql); res = prep.executequery(); while (res.next()){ antall++; catch(sqlexception e){ log("unntak " + e); throw new EJBException(e); finally{ lukkforbindelser(res,prep,forbindelse); return antall; /********************** forretningsmetoder ********************/ //setter inn et beløp på konto public void settinn(int beløp){ log("settinn"); saldo += beløp; //tar ut et beløp fra konto public void taut(int beløp) throws KontoException{ log("taut"); int tmp = saldo - beløp; if (tmp < 0) throw new KontoException("Ikke dekning på konto"); saldo-=beløp; public String hentfornavn(){ log("hentfornavn"); return fornavn; public String hentetternavn(){ log("hentetternavn"); return etternavn; 16

public int hentsaldo(){ log("hentsaldo"); return saldo; public void setfornavn(string nyttfornavn) throws KontoException{ if (nyttfornavn.equals("")) throw new KontoException("Alle må ha et fornavn"); fornavn = nyttfornavn; public void setetternavn(string nyttetternavn) throws KontoException{ if (nyttetternavn.equals("")) throw new KontoException("Alle må ha et etternavn"); etternavn = nyttetternavn; /********************** EJB-metoder **************************/ //henter referansen til DataSource-objektet slik at vi kan bruke databasen //skjer kun ved opprettelse av implementasjonsobjektet public void setentitycontext(entitycontext ctx){ log("setentitycontext"); kontekst = ctx; try{ Context con = new InitialContext(); ds = (DataSource)con.lookup("java:comp/env/jdbc/KontoDB"); catch(namingexception e){ log("unntak " + e); throw new EJBException("Finner ikke datasource-objektet " +e); //metoden kalles etter ejbcreate() og det er først her vi har primærnøkkelen "tilgjengelig" //setter derfor primærnøkkelen kontonr i denne metoden (det kan ikke gjøres i ejbcreate()) public void ejbpostcreate(integer nyttkontonr, String fornavn, String etternavn){ log("ejbpostcreate()"); //henter inn primærnøkkel ved oppstart kontonr = (Integer)kontekst.getPrimaryKey(); public void unsetentitycontext(){ log("unsetentitycontext"); //metoden kalles når dette objektet har fått en ny primærnøkkel //setter derfor kontonr i denne metoden public void ejbactivate(){ log("ejbactivate"); //henter inn primærnøkkel ved enring i denne kontonr = (Integer)kontekst.getPrimaryKey(); //metoden kalles før objektet skal få ny primærnøkkel public void ejbpassivate(){ log("ejbpassivate"); //laster dataene for konto fra databasen public void ejbload(){ log("ejbload"); Connection forbindelse = null; PreparedStatement prep = null; ResultSet res = null; try{ 17

forbindelse = ds.getconnection(); String sql = "select * from konto where kontonr=?"; prep = forbindelse.preparestatement(sql); prep.setint(1,kontonr.intvalue()); res = prep.executequery(); res.next(); fornavn = res.getstring("fornavn"); etternavn = res.getstring("etternavn"); saldo = res.getint("saldo"); // synkroniserer saldo System.out.println(fornavn + etternavn + saldo); catch(sqlexception e){ log("unntak " + e); throw new EJBException(e); finally{//gir fra oss DB forbindelse/frigjør ressurser lukkforbindelser(res, prep,forbindelse); //lagrer dataene til databasen public void ejbstore(){ log("ejbstore"); Connection forbindelse = null; PreparedStatement prep = null; ResultSet res = null; try{ forbindelse = ds.getconnection(); String sql = "update konto set fornavn=?, etternavn=?, saldo=? where kontonr=?"; prep = forbindelse.preparestatement(sql); prep.setstring(1, fornavn); prep.setstring(2, etternavn); prep.setint(3, saldo); prep.setint(4,kontonr.intvalue()); prep.executeupdate(); catch(sqlexception e){ log("unntak " + e); throw new EJBException(e); finally{//gir fra oss DB forbindelse/frigjør ressurser lukkforbindelser(res, prep,forbindelse); //fjerner en kontorad fra databasen public void ejbremove(){ log("ejbremove"); Connection forbindelse = null; PreparedStatement prep = null; ResultSet res = null; try{ forbindelse = ds.getconnection(); String sql = "delete from konto where kontonr=?"; prep = forbindelse.preparestatement(sql); prep.setint(1,kontonr.intvalue()); prep.executeupdate(); catch(sqlexception e){ log("unntak " + e); throw new EJBException(e); finally{//gir fra oss DB forbindelse/frigjør ressurser lukkforbindelser(res, prep,forbindelse); 18

/*********************** hjelpemetoder ****************************/ private void log(string melding){ System.out.println("KontoBMP " + melding); private void lukkforbindelser(resultset res, PreparedStatement prep, Connection forbindelse){ try{ if (res!= null) res.close(); if (prep!= null) prep.close(); if (forbindelse!= null) forbindelse.close(); catch(exception e){ log("unntak i lukkforbindelse " + e); 6 Deploytool og Entity Beans Når du skal lage en Entity Bean så gjør du dette på samme måte som for en Session Bean. Det som blir ulikt er vist i skjermbildene under. Den første forskjellen er at du må velge Bean Type til å være Entity, i stedet for Session (se skjermbildet under). Når du fortsetter prosessen med å lage EJB en vil du etter hvert se skjermbildet under. 19

Her må du velge Persistence Management. Velger du Bean-Managed Persistence (BMP) så må du skrive koden for lagring selv. Velg BMP (vi skal bruke CMP i neste leksjon). I tillegg så må du beskrive hvilken type primærnøkkelen er. Merk at dette ikke kan være en primitiv datatype. Trykk finish. Da er du ferdig med å lage EJB en. I tillegg vil du kanskje trenge en klient til å teste EJB en. Klienten lages på vanlig måte. Eneste endring blir når det gjelder indirekte lookup. Type må settes til Entity i stedet for Session som vi har gjort tidligere (se figuren under). 20

Det siste vi nå trenger å gjøre for å kunne bruke applikasjonen er å spesifisere databasekoblingen til EJB en. Dette gjorde vi også i forrige leksjon. Figuren under viser et eksempel på hvordan dette gjøres for KontoBMPBean. 21

Det var det, da bør applikasjonen være klar til å brukes. 7 Oppgave Lag en Entity Bean med navnet PersonBean. Vedlagt leksjonen følger et sql-skript for å lage tabellen Person (for hvordan bruke databaser, se leksjon 9). Cloudscape er det standard databasessytemet, men du kan også bruke andre om du vil (se leksjon 9 på hvordan du kan lage DataSource-objekter for andre databasesystemer). Tabellen som lages har personnr, fornavn, etternavn og tlf. Lag deg så en Entity Bean for denne tabellen. Lag metoder for å hente og sette feltene i databasen (personnr bør ikke kunne endres da dette er primærnøkkelen). Det må også være mulig å legge til nye personer. I tillegg skal du lage en metode for å hente alle personene, og en metode som returnerer hvor mange personer som finnes. Du trenger ikke å bruke en DAO-klasse i denne øvingen. Dette vil gjøre det enklere å sammenlikne med CMP som vi skal bruke i neste leksjon. Lag også en klient som tester EJB en. I denne oppgaven trenger du ikke å lage lokale-interface. Det som skal leveres er ear-filen for applikasjonen, samt java-filene du har laget. 22