Videregående programmering 6 1. Feilkontroll i klasser uten unntaksobjekter Klasser skal lages sikre. Argumentverdier skal kontrolleres, og eventuelle feil skal rapporteres til klienten. I praksis har vi hittil brukt returverdier false, -1 og null for å varsle klienten om unormale utganger. Eksempel på bruk av enum for å varsle om ulike typer feil fra samme metode er også vist (leksjon 1). Situasjoner der teknikkene foran ikke kan benyttes? 2. Eksempel på try-catch-setningen Lag et lite program som går i løkke inntil et tall er lest inn vhja JOptionPane.showInputDialog(). Se den delen av klassetreet om omfatter Exception-klasser: Object 3. Disse unntakene må håndteres med try og catch, eller kastes ut av metoden Exception Throwable Error 2. Interne feil, kan ikke gjøre noe med disse VirtualMachineError......... IOException SQLException RuntimeException 1 & 2: usjekkede unntak 3: sjekkede unntak 1. Bør skrive programmet slik at disse unngås AritmeticException NullPointerException... 1
3. Behov for unntakshåndtering i databaseprogrammering? class VisDatabaseClient { public static void main(string[] args) throws Exception { String databasedriver =... Class.forName(databasedriver); // laster inn driverklassen String databasenavn =... Connection forbindelse = DriverManager.getConnection(databasenavn); oftest forekommende feil er ClassNotFound- Exception Statement ResultSet res = setning.executequery("select * from person"); int persnr = res.getint("persnr"); String fornavn = res.getstring("fornavn"); String etternavn = res.getstring("etternavn"); System.out.println(persNr + ": " + fornavn + " " + etternavn); res.close(); setning.close(); forbindelse.close(); Connections kan forbli åpne Statements og Connections kan forbli åpne kaster SQLException hvis feil inntreffer ResultSets, Statements og Connections kan forbli åpne Kode med fullstendig unntakshåndtering, se vedlegg 1. Kode der unntakshåndteringen ligger i klassen Opprydder, se vedlegg 2. Ta en titt på klassen Opprydder (lenke fra fagsiden). 2
4. Å legge databasehåndteringen i en egen klasse Database forbindelse: Connection finnalle(): ArrayList<Person> endrenavn(personen: Person): boolean registrernyperson(fornavn: String, etternavn: String): Person slettperson(nr: int): boolean koblenedforbindelse(): void Vis koden for konstruktøren og metodene finnalle() og slettperson(), se vedlegg 3. Metoden registrernyperson() skal du studere i fbm øving 6. Her gjennomgår vi metoden endrenavn(). Enkel testklient, se lenke fra fagsiden. 5. Transaksjonshåndtering Meget kort teorisnutt om hva det er og hvorfor det er nødvendig å ta hensyn til det. Eksempel: create table konto( kontonr varchar(15) not null, navn varchar(30) not null, saldo decimal not null, primary key (kontonr)); insert into konto values ('12345', 'Ole Pettersen', 5250); insert into konto values ('34567', 'Ole Pettersen', 3456); insert into konto values ('45632', 'Ingrid Bø', 1000); Vis kodeeksempel (Transaksjonstest). Vedlegg 4. 3
Vedlgg 1 enkel databasekode med fullstending unntakshåndtering class DatabaseKontaktMedExc { public static void main(string[] args) { String databasedriver = "org.apache.derby.jdbc.clientdriver"; Connection forbindelse = null; Class.forName(databasedriver); String databasenavn = "jdbc:derby://localhost:1527/persondata;user=vprg;password=vprg"; forbindelse = DriverManager.getConnection(databasenavn); catch (Exception e) { System.out.println("Feil 1: " + e); System.exit(0); ResultSet res = null; res = setning.executequery("select * from person"); int persnr = res.getint("persnr"); String fornavn = res.getstring("fornavn"); String etternavn = res.getstring("etternavn"); System.out.println(persNr + ": " + fornavn + " " + etternavn); catch (SQLException e) { System.out.println("Feil 2: " + e); finally { if (res!= null) res.close(); catch (SQLException e) { System.out.println("Feil 3: " + e); finally { if (setning!= null) setning.close(); catch (SQLException e) { System.out.println("Feil 4: " + e); finally { if (forbindelse!= null) forbindelse.close(); catch (SQLException e) { System.out.println("Feil 5:" + e); // finally, lukking av Statement-objekt // finally, lukking av ResultSet-objekt // finally 4
Vedlegg 2 enkel databasekode med fullstendig unntakshåndtering vhja klassen Opprydder class DatabaseKontaktMedExc2 { public static void main(string[] args) { String databasedriver = "org.apache.derby.jdbc.clientdriver"; Connection forbindelse = null; Class.forName(databasedriver); String databasenavn = "jdbc:derby://localhost:1527/persondata;user=vprg;password=vprg"; forbindelse = DriverManager.getConnection(databasenavn); catch (Exception e) { System.out.println("Feil 1: " + e); System.exit(0); ResultSet res = null; res = setning.executequery("select * from person"); int persnr = res.getint("persnr"); String fornavn = res.getstring("fornavn"); String etternavn = res.getstring("etternavn"); System.out.println(persNr + ": " + fornavn + " " + etternavn); catch (SQLException e) { System.out.println("Feil 2: " + e); finally { Opprydder.lukkResSet(res); Opprydder.lukkSetning(setning); Opprydder.lukkForbindelse(forbindelse); // finally 5
Vedlegg 3 klassen Database * Database.java (fullstendig kode, se lenke fra fagsiden) * Feilhåndtering: Konstruktøren kaster unntaksobjekt dersom JDBC-klassen ikke kan * lastes, eller databaseforbindelsen ikke kan åpnes. For øvrig håndteres SQLException * ved en utskrift til konsollet. Slike unntak skyldes programmeringsfeil og sendes ikke * til klienten. import java.util.*; import java.sql.*; import mittbibliotek.person; public class Database { private Connection forbindelse; * Laster JDBC-klassene og åpner databaseforbindelsen. public Database(String dbdriver, String dbnavn) throws Exception { Class.forName(dbDriver); forbindelse = DriverManager.getConnection(dbNavn); catch (Exception e) { Opprydder.skrivMelding(e, "konstruktør"); throw e; * Metode for å lukke forbindelse. Må kalles av klienten før programmet avsluttes. public void koblenedforbindelse() { Opprydder.lukkForbindelse(forbindelse); * Returnerer en tabell-liste som inneholder alle dataene i tabellen person. * Tabell-listen inneholder person-objekter. Listen er sortert etter etternavn. * Returnerer null dersom SQLException er kastet. public ArrayList<Person> finnalle() { ArrayList<Person> alle = new ArrayList<Person>(); String sqlsetning = "select persnr, fornavn, etternavn from person order by etternavn, fornavn"; System.out.println(sqlsetning); ResultSet res = null; res = setning.executequery(sqlsetning); int nr = res.getint("persnr"); String fornavn = res.getstring("fornavn"); 6
String etternavn = res.getstring("etternavn"); Person navnet = new Person(nr, fornavn, etternavn); alle.add(navnet); catch (SQLException e) { Opprydder.skrivMelding(e, "finnalle()"); alle = null; finally { // setningene nedenfor utføres alltid Opprydder.lukkResSet(res); Opprydder.lukkSetning(setning); return alle; * Endrer navn. Det nye navnet samt personens nummer sendes inn som argument. * Returnerer false dersom ingen person med dette nummeret eksisterer, * eller SQLException er inntruffet. public boolean endrenavn(person personen) { //... gjennomgås i forelesningen... * Registrerer ny person. public Person registrernyperson(string fornavn, String etternavn) { // må studeres i fbm øving 6 * Sletter person med gitt nummer. * Returnerer false dersom personen ikke finnes, eller SQLException inntruffet. public boolean slettperson(int nr) { String sqlsetning = "delete from person where persnr = " + nr; System.out.println(sqlsetning); if (setning.executeupdate(sqlsetning) == 0) return false; else return true; catch (SQLException e) { Opprydder.skrivMelding(e, "slettperson()"); finally { Opprydder.lukkSetning(setning); return false; 7
Vedlegg 4 klassen Transaksjonstest * Transaksjonstest.java * Programmet utfører en databasetransaksjon som består av to SQL-setninger. import java.sql.*; class Transaksjonstest { public static void main(string[] args) throws Exception { String databasedriver = "org.apache.derby.jdbc.clientdriver"; Class.forName(databasedriver); String databasenavn = "jdbc:derby://localhost:1527/persondata;user=vprg;password=vprg"; Connection forbindelse = null; ResultSet res = null; forbindelse = DriverManager.getConnection(databasenavn); forbindelse.setautocommit(false); // transaksjon starter res = setning.executequery("select * from konto"); System.out.println("Innhold i databasen før endring: "); System.out.println(res.getString("kontonr") + ", " + res.getstring("navn") + ", " + res.getbigdecimal("saldo")); res.close(); Overfører 1000 kroner fra konto 12345 til konto 45632 setning.executeupdate( "update konto set saldo = saldo - 1000 where kontonr = '12345'"); setning.executeupdate( "update konto set saldo = saldo + 1000 where kontonr = '45632'"); Skriver ut alle dataene res = setning.executequery("select * from konto"); System.out.println("\nInnhold i databasen etter endring: "); System.out.println(res.getString("kontonr") + ", " + res.getstring("navn") + ", " + res.getbigdecimal("saldo")); res.close(); forbindelse.commit(); // alt ok, endringer lagres i databasen catch (SQLException e) { // feil oppstått, alle endringer angres Opprydder.rullTilbake(forbindelse); Opprydder.skrivMelding(e, "Ruller tilbake"); finally { // opprydding uansett utfall av transaksjonen Opprydder.settAutoCommit(forbindelse); Opprydder.lukkResSet(res); Opprydder.lukkSetning(setning); Opprydder.lukkForbindelse(forbindelse); 8