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

20 JDBC - databaseadgang

Indhold:

Kapitlet forudsættes ikke i resten af bogen.

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

Adgang til en database fra Java sker gennem et sæt klasser, der under et kaldes JDBC (Java DataBase Connectivity) – en platformuafhængig pendant til Microsoft ODBC (Open DataBase Connectivity). Klasserne ligger i pakken java.sql, så kildetekstfiler, der arbejder med databaser, skal starte med:

  import java.sql.*;

20.1 Kontakt til databasen

At få kontakt til databasen er måske det sværeste skridt. Det består af to led:

  1. Indlæse databasedriveren

  2. Etablere forbindelsen

Indlæsning af driveren sker, ved at bede systemet indlæse den pågældende klasse, der derefter registrerer sig selv i JDBC-systemets driver-manager. Er det f.eks. en MySQL-database, skriver man:

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

Ofte skal man have en jar-fil (et Java-ARkiv, en samling klasser pakket i zip-formatet) med en driver fra producenten.

For en MySQL-database hedder filen mysql-connector-java-bin.jar. I NetBeans er den pakket med og kan tilføjes til projektet under Project, Libraries, Add Library).

Herefter kan man oprette forbindelsen med (for en MySQL-database):

Connection forb = DriverManager.getConnection(
  "jdbc:mysql://localhost/test?user=bruger&password=ak");

Parametren er en URL til databasen. Den består af en protokol ("jdbc"), underprotokol ("mysql") og noget mere, der afhænger af underprotokollen. I dette tilfælde angiver det, at databasen hedder test og ligger på maskinen selv (localhost) og brugernavn og adgangskode.

20.1.1 JDBC-ODBC-broen under Windows

Med Java under Windows følger en standard JDBC-ODBC-bro med, så man kan kontakte alle datakilder defineret under ODBC. Denne driver indlæses med:

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

Når forbindelsen oprettes, angiver man den ønskede datakildes navn. Husk, at datakildens navn (her "datakilde1") først skal være defineret i Windows' Kontrolpanel under ODBC:

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

Bemærk, at ODBC er en ret langsom protokol. Har du brug for bedre ydelse bør du finde en driver, der kommunikerer direkte med databasen, i stedet for at bruge JDBC-ODBC.

20.2 Kommunikere med databasen

Når vi har en forbindelse, kan vi oprette et "statement"-objekt, som vi kan sende kommandoer og forespørgsler til databasen med:

  Statement stmt = forb.createStatement();

Der kan opstå forskellige exceptions af typen SQLException, der skal fanges.

20.2.1 Kommandoer

SQL-kommandoer, der ikke giver et svar tilbage i form af data, som INSERT, UPDATE, DELETE, CREATE TABLE og DROP TABLE, sendes med executeUpdate()-metoden.

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

import java.sql.*;
public class SimpeltDatabaseeksempel
{
  public static void main(String[] arg) throws Exception
  {
    // Udskift med din egen databasedriver og -URL
    Class.forName("com.mysql.jdbc.Driver");
    Connection forb = DriverManager.getConnection("jdbc:mysql:///test");
    Statement stmt = forb.createStatement();

    stmt.executeUpdate("create table KUNDER (NAVN varchar(32), KREDIT float)" );

    stmt.executeUpdate("insert into KUNDER values('Jacob', -1799)");
    stmt.executeUpdate("insert into KUNDER values('Brian', 0)");
  }
}

Oftest har man data gemt i nogle variabler. Så skal man sætte en streng sammen, der giver den ønskede SQL-kommando:

String navn = "Hans";
int kredit = 500;

// indsæt data fra variablerne navn og kredit
stmt.executeUpdate("insert into KUNDER values('"+navn+"', "+kredit+")");

20.2.2 Forespørgsler

SQL-forespørgslen SELECT udføres med metoden executeQuery():

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

Den giver et ResultSet-objekt, der repræsenterer svaret på forespørgslen (for at få alle kolonner kunne vi også skrive "select * from KUNDER"). Data hentes fra objektet således:

  while (rs.next())
  {
    String navn = rs.getString("NAVN");
    double kredit = rs.getDouble("KREDIT");
    System.out.println(navn+" "+kredit);
  }

Man kalder altså next() for at få næste række i svaret, læser de enkelte celler ud fra kolonnenavnene (eller kolonnenumrene, regnet fra 1 af), hvorefter man går videre til næste række med next() osv. Når next() returnerer false, er der ikke flere rækker at læse.

20.3 Adskille database- og programlogik

Det er en god idé at indkapsle databasearbejdet ét sted, f.eks. i en klasse, sådan at resten af programmet kan fungere, selvom databasens adresse eller struktur skulle ændre sig.

Ofte vil man have en klasse for hver tabel i databasen, sådan at hvert objekt kommer til at svare til en række. Herunder har vi lavet klassen Kunder, svarende til tabellen KUNDER:

public class Kunde
{
  String navn;
  double kredit;

  public Kunde(String n, double k)
  {
    navn = n;
    kredit = k;
  }

  public String toString()
  {
    return navn+": "+kredit+" kr.";
  }
}

Klassen, der varetager forbindelsen til databasen, bør have metoder, der svarer til de kommandoer og forespørgsler, resten af programmet har brug for. Hvis databasen ændrer sig, er det kun denne klasse, der skal rettes i:

import java.sql.*;
import java.util.*;

public class Databaseforbindelse
{
  private Statement stmt;

  public Databaseforbindelse() throws Exception
  {
    Class.forName("oracle.jdbc.driver.OracleDriver");
    Connection forb = DriverManager.getConnection(
      "jdbc:oracle:thin:@oracle.cv.ihk.dk:1521:student","brugernavn","kode");
    stmt = forb.createStatement();
  }

  public void sletAlleData() throws SQLException
  {
    stmt.execute("truncate table KUNDER");
  }

  public void opretTestdata() throws SQLException
  {
    try { // hvis tabellen allerede eksisterer opstår der en SQL-udtagelse
      stmt.executeUpdate(
        "create table KUNDER (NAVN varchar(32), KREDIT float)" );
    } catch (SQLException e) {
      System.out.println("Kunne ikke oprette tabel: "+e);
    }
    stmt.executeUpdate("insert into KUNDER values('Jacob', -1799)");
    stmt.executeUpdate("insert into KUNDER values('Brian', 0)");
  }

  public void indsæt(Kunde k) throws SQLException
  {
    stmt.executeUpdate("insert into KUNDER (NAVN,KREDIT) values('" 
      + k.navn + "', " + k.kredit + ")");
  }

  public ArrayList<Kunde> hentAlle() throws SQLException
  {
    ArrayList<Kunde> alle = new ArrayList<Kunde>();
    ResultSet rs = stmt.executeQuery("select NAVN, KREDIT from KUNDER");
    while (rs.next())
    {
      // brug kolonneindeks i stedet for kolonnenavn
      Kunde k = new Kunde( rs.getString(1), rs.getDouble(2));
      alle.add(k);
    }
    return alle;
  }
}

Klassen lader kalderen om at håndtere de mulige exceptions. Det er fornuftigt, da det også er kalderen, der skal fortælle fejlen til brugeren og evt. beslutte, om programmet skal afbrydes.

Her er et program, der bruger Databaseforbindelse. Først opretter det forbindelsen og henter alle poster, dernæst sletter det alt og indsætter en enkelt post. Hvis der opstår en fejl, udskrives "Problem med database" og programmet afbrydes.

import java.util.*;

public class BenytDatabaseforbindelse
{
  public static void main(String[] arg)
  {
    try {
      Databaseforbindelse dbf = new Databaseforbindelse();

      dbf.opretTestdata(); // fjern hvis tabellen allerede findes
      ArrayList<Kunde> l = dbf.hentAlle();
      System.out.println("Alle data: "+l);
      dbf.sletAlleData();

      dbf.indsæt( new Kunde("Kurt",1000) );
      System.out.println("Alle data nu: "+ dbf.hentAlle());

    } catch(Exception e) {
      System.out.println("Problem med database: "+e);
      e.printStackTrace();
    }
  }
}

Alle data: [Jacob: -1799.0 kr., Brian: 0.0 kr.]
Alle data nu: [Kurt: 1000.0 kr.]

20.4 Opgaver

  1. Ændr SimpeltDatabaseeksempel, så den også laver en SQL-forespørgsel.

  2. Udvid Databaseforbindelse, så den kan søge efter en kunde ud fra kundens navn (antag, at navnet er en primærnøgle, så der ikke kan være flere kunder med samme navn).

  3. Udvid Databaseforbindelse, så den kan give en liste med alle kunder med negativ kredit.

  4. Lav et program, der holder styr på en musiksamling vha. en database. Databasen skal have tabellen UDGIVELSER med kolonnerne år, navn, gruppe og pladeselskab. Opret en tilsvarende klasse, der repræsenterer en Udgivelse (int år, String navn, String gruppe, String pladeselskab). Lav en passende Databaseforbindelse og et (evt. grafisk) program, der arbejder med musikdatabasen.

  5. Ret databasen i forrige opgave til at have tabellen UDGIVELSER med kolonnerne år, navn og gruppeID, tabellen GRUPPER med kolonnerne gruppeID, navn, pladeselskab. Hvordan skal Databaseforbindelse ændres? Behøves der blive ændret i resten af programmet? Hvorfor?

  6. Udvid programmet, så hver gruppe har en genre som f.eks. rock, tekno, klassisk (tabellen GRUPPER udvides med genreID og tabellen GENRER oprettes med kolonnerne genreID og navn).

20.5 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.

20.5.1 Forpligtende eller ej? (commit)

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.

20.5.2 Optimering

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.

20.5.3 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.

20.5.4 Opdatering og navigering i ResultSet

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/ - Forord af Jacob Nordfalk.
Licens og kopiering under Åben Dokumentlicens (ÅDL) hvor intet andet er nævnt (79% af værket).

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