Videregående programmering i Java

Dag 11 - databaser

De resterende grupper fremlægger projekt

Projektvejledning

JDBC og dens brug af designmønstre

Evt.: Avanceret JDBC

Evt.: Objektpersistens og JDO - Java Data Objects

Designmønstrenes brug i standardbibliotekerne

Læsning: VP 8

Udgår: Internationalisering (VP 7)

Dette materiale er under Åben Dokumentlicens, se http://www.sslug.dk/linuxbog/licens.html

Sidste gang - spørgsmål?

Dag 10 - Flertrådet programmering

Fremlæggelse af programmering/status for projekter

Dokumentation med javadoc

Flertrådede designmønstre: Arbejdstråd, Producent-Konsument, Trådpulje

Evt.: Kommunikation mellem processer og tråde i et GUI-program

Evt.: Synkronisering af processer og tråde, semaforer [TJT] om tråde

Evt.: Monitorer, Java synkronisering, Deadlocks

Persistensproblemet
Strategier til databaseadgang

Rigtig mange muligheder, f.eks.:

JDBC-kode blandet sammen med resten af koden

JDBC-kode i dedikerede klasser

JDBC RowSet - ResultSet, der også opdaterer databasen

CachedRowSet - kan eksistere løsrevet fra databasen

WebRowSet giver XML, der kan transformeres, f.eks. med

XSLT (XSL style sheet)

Javas XML-behandling

EJB - Enterprise JavaBeans - ét serverobjekt pr. række

JDO - Java Data Objects - ét objekt pr. række

Proprietære løsninger

Giver mulighed for grafiske databasekomponenter

Oracle JDeveloper ADF - Application Developer Framework

Borland JBuilder: DataModule

IBM WebSphere Studio

Bedste løsning? Afhænger af omstændighederne!

Hvor omfattende databasedelen af ens applikation er

Hvor meget man forventer den senere skal vedligeholdes.

JDBC – databaseadgang

JDBC – databaseadgang

Lave JDBC-ODBC-bro til Access-fil

Eksempel:

1. Denne computer

2. Kontrolpanel

3. Administration

4.

5.

6

7

Forbindelse til database (MySQL)

Installér MySQL

Hent fra mysql.com

test-database god i starten

Grafiske værktøjer

Installér JDBC-driver

Connector/J fra mysql.com

Læg JAR-fil i java/jre/lib/ext/

Kontakt test-database:

Forberedte SQL-kommandoer

import java.sql.*;

public class ForberedtSQL {

public static void main(String[] arg) throws Exception

{

Class.forName("com.mysql.jdbc.Driver");

Connection con = DriverManager.getConnection("jdbc:mysql:///test");

// Forbered kommandoerne til databasen, f.eks. i starten af programmet:

PreparedStatement indsætPstm = con.prepareStatement(

"INSERT INTO kunder (navn,kredit) VALUES(?, ?)");

PreparedStatement hentPstm = con.prepareStatement(

"SELECT navn, kredit FROM kunder WHERE navn=?");

// under programudførelsen kan de forberedte kommandoer udføres mange gange:

for (int i=0; i<100; i++)

{

indsætPstm.setString(1, "Brian");

indsætPstm.setInt(2, i);

indsætPstm.execute();

indsætPstm.setString(1, "Hans' venner"); // bemærk ' i strengen

indsætPstm.setInt(2, 1042+i);

indsætPstm.execute();

hentPstm.setString(1, "Hans' venner"); // bemærk ' i SQL-forespørgslen

ResultSet rs = hentPstm.executeQuery();

// man løber igennem svaret som man plejer

while (rs.next())

{

String navn = rs.getString(1);

double kredit = rs.getDouble(2);

System.out.println(navn+" "+kredit);

}

}

}

}

Samlede batch-opdateringer

import java.sql.*;

public class Batchopdateringer

{

public static void main(String[] arg) throws Exception

{

Class.forName("com.mysql.jdbc.Driver");

Connection con = DriverManager.getConnection("jdbc:mysql:///test");

PreparedStatement pstmt = con.prepareStatement(

"INSERT INTO kunder (navn,kredit) VALUES(?, ?)");

pstmt.setString(1, "Hans");

pstmt.setInt(2, 142);

pstmt.addBatch();

pstmt.setString(1, "Grethe");

pstmt.setInt(2, 242);

pstmt.addBatch();

// send ændringer til databasen

pstmt.executeBatch();

}

}

Uden automatisk commit

import java.sql.*;

public class UdenAutocommit

{

public static void main(String[] arg) throws Exception

{

Class.forName("oracle.jdbc.driver.OracleDriver");

Connection forb = DriverManager.getConnection(

"jdbc:oracle:thin:@ora.javabog.dk:1521:student","jacob","jacob");

try {

forb.setAutoCommit(false);

Statement stmt = forb.createStatement();

stmt.executeUpdate("insert into KUNDER(NAVN,KREDIT) values('Jacob',-17)");

stmt.executeUpdate("insert into KUNDER(NAVN,KREDIT) values('Brian', 0)");

// flere transaktioner ...

System.err.println("Alt gik godt, gør ændringerne forpligtigende");

forb.commit();

}

catch (Exception e)

{

e.printStackTrace();

System.err.println("Noget gik galt! Annullerer ændringerne...");

forb.rollback();

}

finally

{

// Husk at sætte auto-commit tilbage, af hensyn til andre transaktioner

forb.setAutoCommit(true);

}

}

}

import java.sql.*;

public class BenytMetadataOgUdskrivTabel

{

/** Udskriver et ResultSet pænt. Finder selv ud af kolonnenavnene. */

public static void udskriv(String titel, ResultSet rs) throws Exception {

ResultSetMetaData rsmd = rs.getMetaData();

int antalKolonner = rsmd.getColumnCount();

System.out.println();

System.out.println(" --------- " + titel + " ("+antalKolonner+" kolonner)");

// udskriv kolonnenavnene

for (int i=1; i<=antalKolonner; i++) skrivFormateret(rsmd.getColumnName(i));

System.out.println();

// udskriv cellerne i hver række

while (rs.next())

{

for (int i=1; i<=antalKolonner; i++) skrivFormateret(""+rs.getString(i));

System.out.println();

}

System.out.println(" -----------------------------");

}

public static void main(String[] arg) throws Exception

{

Class.forName("oracle.jdbc.driver.OracleDriver");

Connection forb = DriverManager.getConnection(

"jdbc:oracle:thin:@ora.javabog.dk:1521:student","jacob","jacob");

DatabaseMetaData dmd = forb.getMetaData();

System.out.println("DatabaseProductName = "+dmd.getDatabaseProductName());

System.out.println("DriverName = "+dmd.getDriverName());

System.out.println("MaxRowSize = "+dmd.getMaxRowSize());

ResultSet rs = dmd.getTables(null, "JANO", "%", null);

udskriv("tabeller i databasen", rs);

Statement stmt = forb.createStatement();

rs = stmt.executeQuery("select * from KUNDER");

udskriv("kunder", rs);

}

public static void skrivFormateret(String str) {

System.out.print(str);

String KOL = " | ";

if (str.length()<KOL.length()) System.out.print(KOL.substring(str.length()));

}

}

Metadata

import java.sql.*;

public class BenytMetadataOgUdskrivTabel

{

/** Udskriver et ResultSet pænt. Finder selv ud af kolonnenavnene. */

public static void udskriv(String titel, ResultSet rs) throws Exception {

ResultSetMetaData rsmd = rs.getMetaData();

int antalKolonner = rsmd.getColumnCount();

System.out.println();

System.out.println(" --------- " + titel + " ("+antalKolonner+" kolonner)");

// udskriv kolonnenavnene

for (int i=1; i<=antalKolonner; i++) skrivFormateret(rsmd.getColumnName(i));

System.out.println();

// udskriv cellerne i hver række

while (rs.next())

{

for (int i=1; i<=antalKolonner; i++) skrivFormateret(""+rs.getString(i));

System.out.println();

}

System.out.println(" -----------------------------");

}

public static void main(String[] arg) throws Exception

{

Class.forName("oracle.jdbc.driver.OracleDriver");

Connection forb = DriverManager.getConnection(

"jdbc:oracle:thin:@ora.javabog.dk:1521:student","jacob","jacob");

DatabaseMetaData dmd = forb.getMetaData();

System.out.println("DatabaseProductName = "+dmd.getDatabaseProductName());

System.out.println("DriverName = "+dmd.getDriverName());

System.out.println("MaxRowSize = "+dmd.getMaxRowSize());

ResultSet rs = dmd.getTables(null, "JANO", "%", null);

udskriv("tabeller i databasen", rs);

Statement stmt = forb.createStatement();

rs = stmt.executeQuery("select * from KUNDER");

udskriv("kunder", rs);

}

public static void skrivFormateret(String str) {

System.out.print(str);

String KOL = " | ";

if (str.length()<KOL.length()) System.out.print(KOL.substring(str.length()));

}

}

Metadata

Opdatere og navigere i ResultSet

De fleste ResultSet har metoder til at bevæge sig aktivt rundt i svaret og endda opdatere databasen gennem svaret.

Det gøres med f.eks.:

Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,

ResultSet.CONCUR_UPDATABLE);

ResultSet rs = stmt.executeQuery("select NAVN, KREDIT from KUNDER");

Derefter kan man navigere rundt i ResultSet-objektet med f.eks.:

rs.absolute(3); // går til 3. række i svaret (regnet fra 1 af)

rs.previous(); // går en række tilbage (modsatte af next())

rs.first(); // går til starten af svaret (svarende til rs.absolute(1))

rs.relative(3); // går 3 rækker frem, dvs. til 4. række

int r = rs.getRow();// giver hvilken række vi nu er i (her returneres 4)

Man kan ændre i data med f.eks.:

rs.updateString("NAVN", "Jakob"); // ændrer kundens navn til Jakob

rs.updateRow(); // opdaterer rækken i databasen

rs.moveToInsertRow(); // flyt til speciel indsættelses-række

rs.updateString("NAVN", "Søren"); // sæt navn

rs.updateDouble("KREDIT", 1000); // sæt kredit

rs.insertRow(); // indsæt rækken i databasen

rs.moveToCurrentRow(); // gå væk fra speciel indsættelsesrække

Derudover findes cancelRowUpdates(), der annullerer opdateringer i en række, deleteRow(), der sletter den aktuelle række fra både svaret og databasen, refreshRow(), der opfrisker ResultSet-objektet med de nyeste data.

JDBC RowSet

RowSet = et sæt rækker hentet fra databasen

=ResultSet+oprindelse+ændringer

RowSet-objekter giver mulighed ny arbejdsform

Man behøver kun at spørge på data (f.eks. med SELECT) hvorefter man får et RowSet-objekt.

Når man skal opdatere nogle rækker, slette eller oprette nye, opererer man blot det eksisterende RowSet-objekt

Slut med at rode med INSERT-, DELETE- eller UPDATE-sætninger i SQL.

Flere undertyper af RowSet i JDK 1.5

JdbcRowSet =ResultSet+oprindelse+ændringer

CachedRowSet =rækkerne er gemt i hukommelsen

kan altså eksistere afbrudt fra databasen!

Undertyper: FilteredRowSet og JoinRowSet

WebRowSet =CachedRowSet repræsenteret som XML

JdbcRowSet

CachedRowSet

WebRowSet

De resterende grupper fremlægger projekt

Evt.: JDO - Java Data Objects

import javax.jdo.*;

private PersistenceManagerFactory pmf = null;

// initialisering af pmf (udbyderspecifik, ikke vist)

PersistenceManager pm;

Transaction transaction;

// oprette et objekt (beskrevet i XML-dokumentet) og gemme det

Person p = new Person("Jacob", "Nordfalk", "nordfalk@mobilixnet.dk");

Object id;

pm = pmf.getPersistenceManager();

transaction = pm.currentTransaction();

pm.makePersistent(p);

id = pm.getObjectId(p);

transaction.commit();

pm.close();

...

// hente et objekt (der ikke skal ændres)

pm = pmf.getPersistenceManager();

p = (Person) pm.getObjectById(id, false);

pm.close();

// ændre objekter

pm = pmf.getPersistenceManager();

transaction = pm.currentTransaction();

p = (Person) pm.getObjectById(id, false);

person.setName("Troels");

transaction.commit();

pm.close();

...

// slette objekter

pm = pmf.getPersistenceManager();

transaction = pm.currentTransaction();

p = (Person) pm.getObjectById(id, false);

pm.deletePersistent(p);

transaction.commit();

pm.close();

Resumé af skabende designmønstre

Fabrikeringsmetode

en metode, der fabrikerer objekter for klienten (i stedet for at klienten selv opretter dem med new)

Fabrik

et objekt med en fabrikeringsmetode

Singleton

sikring af, at der kun eksisterer ét objekt af en bestemt slags

Abstrakt Fabrik/Toolkit

er en Fabrik med nedarvinger, der sørger for objektoprettelsen. Hvilken nedarving der anvendes, bestemmes af en fabrikeringsmetode

Bygmester

simplificerer oprettelsen af nogle relaterede objekter ved at oprette og konfigurere objekterne (evt. trinvist) for klienten

Prototype

objekter oprettes ud fra eksisterende skabelon-objekter

Objektpulje

genbrug de samme objekter igen og igen ved at huske dem i en pulje

Hyppigt anvendte designmønstre

Proxy

få metodekald til at gå gennem et mellem-objekt (proxyen), der modtager metodekald på vegne af (fungerer som en erstatning) for det rigtige objekt og kalder videre i det rigtige objekt

Herunder Virtuel Proxy/Doven Initialisering

udskyde oprettelsen af det rigtige objekt til første gang, der er brug for det

Adapter

et hjælpeobjekt, der får et objekt til at passe ind i et system ved at fungere som omformer mellem objektet og systemet

Iterator

hjælper med at gennemløbe nogle data

Facade

forenkler brugen af et sæt objekter ved at give en simplificeret grænseflade til dem

Observatør/Lytter

at objekter kan 'abonnere' på, at en ting (hændelse) sker

Dynamisk Binding

at understøtte "plugin"-klasser, der kan indlæses under kørslen og dermed udvide programmet, efter at det er skrevet

Resumé af andre designmønstre

Uforanderlig

objektet kan ikke ændres, når det først er oprettet

Fluevægt

begræns antallet af objekter ved at sørge for, at der ikke bliver oprettet objekter med de samme data. I stedet er der mange referencer til de samme (unikke) objekter

Filter

objekter, der "filtrerer" en strøm af data. Filtrene kan kombineres vilkårligt

Lagdelt Initialisering

klienten opretter et objekt direkte, og dette objekt opretter eller fremskaffer det, der i virkeligheden skal bruges og videredelegerer det meste af arbejdet til det

Komposit/Rekursiv Komposition

skabe et meget fleksibelt objekthierarki, ved at definere objekter, der kan indeholde andre objekter inkl. sin egen slags

Kommando

registrér brugerens ændringer af data, sådan at de kan fortrydes igen

JDBC og dens brug af designmønstre

Videregående programmering i Java

Præsentatoner fra kurset

2004-08-30T12:23:21