javabog.dk  |  << forrige  |  indhold  |  næste >>  |  programeksempler  |  om bogen

8 Databaser (JDBC)


8.1 Basisfunktioner i JDBC 130

8.1.1 Indlæse driveren 130

8.1.2 Etablere forbindelsen 130

8.1.3 Kommunikere med databasen 131

8.2 Forpligtigende eller ej? (commit) 132

8.3 Optimering 133

8.3.1 Bruge den rigtige databasedriver 133

8.3.2 Lægge opdateringer i kø (batch-opdateringer) 133

8.3.3 På forhånd forberedt SQL 134

8.3.4 Kalde gemte procedurer i databasen 135

8.4 Avanceret 136

8.4.1 Opdatering og navigering i ResultSet-objekter 136

8.4.2 Metadata 136

8.4.3 Metadata om databasen 136

8.4.4 Metadata om svaret på en forespørgsel 136

8.4.5 Eksempel - udskrive en vilkårlig tabel 137

8.4.6 Persistering af objekter - JDO 138

Dette kapitel forudsætter lidt kendskab til databaser og databasesproget SQL (Structured Query Language), og at man har en fungerende database, som man ønsker adgang til fra Java.

8.1 Basisfunktioner i JDBC

Et program, der bruger JDBC, skal basalt set udføre følgende handlinger: Indlæse driveren, etablere forbindelsen og dernæst kommunikere med databasen.

8.1.1 Indlæse driveren

Det gøres ved at indlæse en driver-klasse:

  Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

Denne klasse sørger for at registrere sig hos java.sql.DriverManager.

Med Java under Windows følger en standard JDBC-ODBC-bro med, så man kan kontakte alle datakilder, der er defineret under ODBC.

Er det en anden database, skal man have en jar-fil (et Java-ARkiv, en samling klasser pakket i zip-formatet) med en driver fra producenten, og man skal indlæse en anden driver-klasse fra jar-filen (der skal ligge i CLASSPATH).

De nyeste drivere kan findes på http://java.sun.com/jdbc, men følger ofte med når man anskaffer sig en database.

For at få indlæst en driver til en Oracle-database skriver man:

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

Driver-filen, der følger med Oracles produkter, hedder typisk classes12.zip.

For at få indlæst en driver til en MySQL-database skriver man:

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

Driver-filen til MySQL (kaldet Connector/J) kan hentes på http://mysql.com.

8.1.2 Etablere forbindelsen

Herefter kan man oprette forbindelsen med (for en ODBC-driver):

  Connection forb = DriverManager.getConnection("jdbc:odbc:datakilde1");

Parameteren er en URL til databasen, som driver-klassen genkender. Husk, at datakildens navn (her "datakilde1") først skal være defineret i Windows' Kontrolpanel under ODBC.

Er det en Oracle-database, kunne man skrive:

  Connection forb = DriverManager.getConnection(
               "jdbc:oracle:thin:@ora.javabog.dk:1521:student","jacob","jacob");

Hvorefter man skulle have oprettet forbindelsen til databasen 'student' på maskinen 'ora.javabog.dk' port 1521 med brugernavn 'jacob' og adgangskode 'jacob'.

Er det en MySQL-database, kunne man skrive:

  Connection c = DriverManager.getConnection("jdbc:mysql:///jacob","root","xyz");

Hvorefter man skulle have oprettet forbindelsen til databasen 'jacob' på den lokale maskine med brugernavn 'root' og adgangskode 'xyz'.

8.1.3 Kommunikere med databasen

Når man har oprettet en forbindelse, kan man oprette et Statement-objekt, som man kan sende kommandoer og forespørgsler til databasen med (svarer til en SQL-kommandolinje):

  Statement stmt = forb.createStatement();

Her opretter vi f.eks. tabellen KUNDER og indsætter et par rækker:

import java.sql.*;
public class Databasekommunikation
{
  public static void main(String[] arg) throws Exception
  {
    Class.forName("oracle.jdbc.driver.OracleDriver");
    System.out.println("Driver indlæst");

    Connection forb = DriverManager.getConnection(
               "jdbc:oracle:thin:@ora.javabog.dk:1521:student","jacob","jacob");
    System.out.println("Forbindelse oprettet");

    Statement stmt = forb.createStatement();

    // forsøg at slette tabel - hvis den ikke findes opstår en fejl som fanges
    try { stmt.executeUpdate("drop table KUNDER"); } catch (Exception e) {}

    // opret tabel
    stmt.executeUpdate("create table KUNDER (NAVN varchar(32), KREDIT number)");
    System.out.println("Tabel oprettet");

    // tilføj data
    stmt.executeUpdate("insert into KUNDER values('Jacob', -1799)");

    // brug helst navngivne kolonner. Hvis man senere skulle finde på at
    // udvide tabellen med flere kolonner, vil SQL-kommandoen stadig virke!
    stmt.executeUpdate("insert into KUNDER(NAVN,KREDIT) values('Brian', 0)");

    // indsæt data fra variabler
    String navn = "Hans";
    double kredit = 500;
    stmt.executeUpdate(
               "insert into KUNDER(NAVN,KREDIT) values('"+navn+"', "+kredit+")");

    // forespørgsler
    ResultSet rs = stmt.executeQuery("select NAVN, KREDIT from KUNDER");
    while (rs.next())
    {
      navn = rs.getString("NAVN");
      kredit = rs.getDouble("KREDIT");
      System.out.println(navn+" "+kredit);
    }
  }
}

Driver indlæst
Forbindelse oprettet
Tabel oprettet
Jacob -1799.0
Brian 0.0
Hans 500.0

Se også afsnit 12.8 for en beskrivelse af alle klasserne, der er til rådighed til kommunikation med en database.

8.2 Forpligtigende eller ej? (commit)

Normalt er alle SQL-kommandoer gennem JDBC automatisk forpligtigende (eng. auto-committing), dvs. de kan ikke annulleres, hvis en senere SQL-kommando går galt.

Vil man slå automatisk forpligtigelse fra, kan det gøres ved at kalde setAutoCommit() på forbindelsen. Derefter vil transaktioner (SQL-kommandoer) ikke være forpligtigende, og de kan annulleres ved at kalde rollback() på forbindelsen. Når man ønsker, at transaktionerne skal træde i kraft, kalder man commit(), og først herefter er transaktionerne endeligt udført på databasen og dermed forpligtigende.

Oftest anvendes denne facilitet i forbindelse med flere transaktioner, der enten alle skal gennemføres eller alle annulleres, f.eks.:

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);
    }
  }
}

Alt gik godt, gør ændringerne forpligtigende

8.3 Optimering

Ønsker man hurtigere kommunikation med databasen, er der en række ting, man kan gøre.

8.3.1 Bruge den rigtige databasedriver

JDBC-drivere findes i fire typer:

De hurtigste drivere er type 2, men ofte kan en type 4 driver blive næsten lige så hurtig. En liste med over hundrede tilgængelige drivere kan findes på http://java.sun.com/jdbc.

8.3.2 Lægge opdateringer i kø (batch-opdateringer)

Skal man lave mange opdateringer, kan det være en fordel at sende dem af sted som én samlet enhed (eng.: batch) i stedet for som normalt at vente på, at hver opdatering skal gennemføres, før den næste kan sendes.

import java.sql.*;
public class Batchopdateringer
{
  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");

    Statement stmt = forb.createStatement();
    stmt.addBatch("insert into KUNDER(NAVN,KREDIT) values('Jacob', -1799)");
    stmt.addBatch("insert into KUNDER(NAVN,KREDIT) values('Brian', 0)");

    // send først nu ændringer til databasen
    stmt.executeBatch();
  }
}

8.3.3 På forhånd forberedt SQL

Det er klart, at der i koden vist ovenfor bruges noget tid på at fortolke SQL-kommandoen hver gang kaldet foretages.

I stedet for at oprette en SQL-kommandolinje med createStatement(), kan man bruge metoden prepareStatement(), hvor man angiver SQL-kommandoen én gang under opstart af programmet, og derefter kan udføre kommandoen flere gange.

import java.sql.*;
public class ForberedtSQL
{
  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");

    // Forbered kommandoerne til databasen i starten af programmet:

    PreparedStatement indsæt = forb.prepareStatement(
                                "insert into KUNDER(NAVN,KREDIT) values(?, ?)");

    PreparedStatement hent = forb.prepareStatement(
                                             "select NAVN, KREDIT from KUNDER");

    // under programudførelsen kan forberedte kommandoer udføres mange gange:
    indsæt.setString(1, "Jacob");
    indsæt.setInt(2, -1799);
    indsæt.execute();

    indsæt.setString(1, "Brian");
    indsæt.setInt(2, 0);
    indsæt.execute();

    // som før kan opdateringerne også lægges i kø:
    indsæt.setString(1, "Hans");
    indsæt.setInt(2, 142);
    indsæt.addBatch();

    indsæt.setString(1, "Grethe");
    indsæt.setInt(2, 242);
    indsæt.addBatch();

    // send ændringer til databasen
    indsæt.executeBatch();

    ResultSet rs = hent.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);
    }
  }
}

Jacob -1799.0
Brian 0.0
Hans 142.0
Grethe 242.0

8.3.4 Kalde gemte procedurer i databasen

Større databaser understøtter 'stored procedures' - procedurer gemt i databasen. Disse procedurer kan udføres hurtigere, da databasen på forhånd kan optimere, hvordan SQL-kaldene skal foregå.

En gemt procedure kan kaldes med et CallableStatement (her forestiller vi os, at der på forhånd er oprettet procedurerne indsaetkunde og hentkunder i databasen):

  CallableStatement indsætP =  forb.prepareCall("call indsaetkunde(?, ?)");
  CallableStatement hentP =  forb.prepareStatement("?= hentkunder");

Resten af arbejdet foregår som med PreparedStatement:

  indsætP.setString(1, "Jacob");
  indsætP.setInt(2, -1799);
  indsætP.execute();
  indsætP.setString(1, "Brian");
  indsætP.setInt(2, 0);
  indsætP.execute();

  ResultSet rs = hentP.executeQuery();
  ...

8.4 Avanceret

Dette afsnit er ikke omfattet af Åben Dokumentslicens.
Du skal købe bogen for at måtte læse dette afsnit.
Jeg erklærer, at jeg allerede har købt bogen
Jeg lover at anskaffe den i nær fremtid.

8.4.1 Opdatering og navigering i ResultSet-objekter

Dette afsnit er ikke omfattet af Åben Dokumentslicens.
Du skal købe bogen for at måtte læse dette afsnit.
Jeg erklærer, at jeg allerede har købt bogen
Jeg lover at anskaffe den i nær fremtid.

8.4.2 Metadata

Dette afsnit er ikke omfattet af Åben Dokumentslicens.
Du skal købe bogen for at måtte læse dette afsnit.
Jeg erklærer, at jeg allerede har købt bogen
Jeg lover at anskaffe den i nær fremtid.

8.4.3 Metadata om databasen

Dette afsnit er ikke omfattet af Åben Dokumentslicens.
Du skal købe bogen for at måtte læse dette afsnit.
Jeg erklærer, at jeg allerede har købt bogen
Jeg lover at anskaffe den i nær fremtid.

8.4.4 Metadata om svaret på en forespørgsel

Dette afsnit er ikke omfattet af Åben Dokumentslicens.
Du skal købe bogen for at måtte læse dette afsnit.
Jeg erklærer, at jeg allerede har købt bogen
Jeg lover at anskaffe den i nær fremtid.

8.4.5 Eksempel - udskrive en vilkårlig tabel

Dette afsnit er ikke omfattet af Åben Dokumentslicens.
Du skal købe bogen for at måtte læse dette afsnit.
Jeg erklærer, at jeg allerede har købt bogen
Jeg lover at anskaffe den i nær fremtid.

8.4.6 Persistering af objekter - JDO

Dette afsnit er ikke omfattet af Åben Dokumentslicens.
Du skal købe bogen for at måtte læse dette afsnit.
Jeg erklærer, at jeg allerede har købt bogen
Jeg lover at anskaffe den i nær fremtid.
javabog.dk  |  << forrige  |  indhold  |  næste >>  |  programeksempler  |  om bogen
http://javabog.dk/ - af Jacob Nordfalk.
Licens og kopiering under Åben Dokumentlicens (ÅDL) hvor intet andet er nævnt (71% af værket).

Ønsker du at se de sidste 29% af dette værk (362838 tegn) skal du købe bogen. Så får du pæne figurer og layout, stikordsregister og en trykt bog med i købet.