inhoudstafel en auteursrecht
* STER *


6. Databases

6.1 Platform-onafhankelijke toegang tot databases

Java is tegelijkertijd een programmeertaal en een programmeeromgeving, beiden speciaal ontworpen om zo veel mogelijk onafhankelijk te zijn van de omgeving waarop ze draaien: de processor, het besturingssysteem, de randapparatuur.

Databases vormen in dat opzicht een bijzondere uitdaging. Met de term relationele database bedoelt men gewoonlijk: een geïntegreerd gegevensbeheersysteem waarvan de opbouw gebaseerd is op de wiskundige theorie van het relationele gegevensbankmodel. Dat wil ondermeer zeggen dat alle gegevens en hun onderlinge verbanden in tabellen worden opgeslagen. Er bestaan relationele databases van veel verschillende merken, en elk hebben ze hun eigen sterke en zwakke punten. Belangrijke marktspelers zijn: Oracle, Sybase, Informix, Ingres. Ook Microsoft speelt een rol met het low-end product MS Access.

De toegang tot databases is slechts gedeeltelijk gestandaardiseerd. Weliswaar ondersteunen de meeste producten de bevragingstaal SQL (structured query language), maar:

Om aan deze verscheidenheid tegemoet te komen, bevat het Java-platform een faciliteit die een uniforme interface biedt aan programmeurs, en die de verscheidenheid van database-systemen opvangt door de installatie van afzonderlijke driver-software. Dit systeem heet JDBC, wat naar verluidt niet de afkorting is van "Java Database Connectivity".

JDBC is verre van de eerste poging om database-toegang voor programmeurs te standaardiseren. Een vroegere populaire standaard, echter uitsluitend bruikbaar voor Windows-clients, is Microsofts Open Database Connectivity (ODBC). Ook hier worden merk-specifieke details van de programmeur afgeschermd via een reeks drivers en data sources. Deze laatsten zijn bereikbaar via de applicatie "ODBC32" in het besturingspaneel (control panel).

Om op Windows-platformen vanuit een Java-programma reeds gebruik te kunnen maken van de bestaande ODBC-drivers, heeft Sun een speciale driver geschreven bovenop het ODBC-systeem, de zogenaamde JDBC-ODBC-brug. Hoewel deze driver niet bedoeld is voor professioneel gebruik, en slechts de oudste versie van JDBC (1.0) ondersteunt, kan hij handig van pas komen om een goedkope leeromgeving voor JDBC te installeren, bijvoorbeeld in combinatie met MS Office Professional.

Naargelang van de client/server architectuur onderscheiden we vier soorten JDBC-drivers.

Een tamelijk volledige lijst van beschikbare JDBC-drivers is te raadplegen op de URL

http://industry.java.sun.com/products/jdbc/drivers

6.2 Voorbeeld van een driver-installatie (uitsluitend Windows)

In deze paragraaf beschrijven we kort de nodige handelingen om een plaatselijke JDBC-verbinding te maken naar een MS Access 97 gegevensbank op een MS Windows 95 computer. De procedure is nauwelijks verschillend bij recentere versies van MS Access en MS Windows.

De JDBC-ODBC-brug wordt automatisch geïnstalleerd als onderdeel van het Java Platform 2, version 1.3. In paragraaf 6.3 zullen we een voorbeeldprogramma presenteren dat gebruik maakt van een ODBC-datasource met de naam "Bibliotheek".

Laten we een dergelijke datasource creëren via het Configuratiescherm (Control Panel). Veronderstel dat er reeds een MS Access-database bestaat met de padnaam C:\Library\Data\Loan.mdb

Open het configuratiescherm via Start/Instellingen/Configuratiescherm en dubbelklik het icoon "32 bit ODBC". Het kan ook zijn dat dit icoon "ODBC" heet, en het getal 32 in zijn tekening verwerkt draagt. Je krijgt ongeveer het volgende venster te zien.

ODBC Data Source Administrator/User DSN

De afkorting DSN staat voor data source name. Klik op Add... om een nieuwe data source te creëren. In de dialoog die daarop volgt, selecteer je de "Microsoft Access Driver" en je klikt op Voltooien. Vul dan als Data Source Name: de naam Bibliotheek in,

ODBC Microsoft Access 97 Setup/Data Source Name: Bibliotheek

en klik op Select... om via een gewone bestandsdialoog het pad C:\Library\Data\Loan.mdb te specificeren.

Select Database/Database Name Loan.mdb/Directories: c:\Library\Data

Klik op OK en verifieer dat het juiste pad is geselecteerd. Klik nogmaals op OK en verifieer dat de datasource "Bibliotheek" inderdaad in het lijstje van DSNs voorkomt. Klik tenslotte op OK om het ODBC-configuratieprogramma af te sluiten.

Opmerking. Op sommige systemen heeft de ODBC-driver voor MS Access een vervelende bug waardoor soms onterecht de foutmelding "Invalid file path" ontstaat. Creëer in dat geval een nieuwe database met de knop Create... en hernoem achteraf de gegeven database met de nieuwe naam die je in het "create" dialoogvenster hebt gegeven.

6.3 Bevraging van een database vanuit een programma

Een Java-programma dat contact onderhoudt met een JDBC-database, moet de volgende acties ondernemen:

Het laden van de JDBC-driver gebeurt door een aanroep van de methode Class.forName met een String-parameter die de volledige, onafgekorte klassennaam van de driver bevat. In het geval van de ODBC-JDBC-brug is dat

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

Om een verbinding te maken met de database, declareren we een veranderlijke van het type Connection. Vervolgens roepen we de methode DriverManager.getConnection op met drie String-parameters:

  1. een zogenaamde database URL; in het geval van ODBC is dat "jdbc:odbc:<data source>", waar <data source> de naam is van de datasource die naar onze database verwijst
  2. een eventuele login-naam
  3. een eventueel wachtwoord

Om ons programma te verbinden met de database uit paragraaf 6.2, gebruiken we de opdrachtregels

Connection verbinding;
verbinding = DriverManager.getConnection("jdbc:odbc:Bibliotheek", "", "");

Maak daarna een (leeg) opdrachtobject van de klasse Statement met een oproep van de methode createStatement() (zonder parameters) van de klasse Connection.

Statement opdracht;
opdracht = verbinding.createStatement();

Van de klasse Statement zijn nu twee verschillende methoden van belang. De methode executeUpdate neemt als parameter een tekststreng met een SQL-opdracht die geen resultaattabel moet teruggeven, bijvoorbeeld een toevoeging of verwijdering van rijen in een tabel. De methode executeQuery neemt eveneens een tekstparameter, dit keer met een SQL SELECT-opdracht. De terugkeerwaarde is van het type ResultSet en bevat de resultaattabel van de vraagstelling.

De volgende opdrachtregel voegt een rij toe aan de tabel BOOK, in de veronderstelling dat die tabel bestaat en kolommen NR en TITLE heeft.

opdracht.executeUpdate("INSERT INTO BOOK (NR, TITLE) VALUES (12345, 'Dune')");

Voor een korte inleiding tot het gebruik van INSERT en andere SQL-opdrachten verwijzen we naar de online cursustekst "Structured Query Language" van Marc Andries (zie onze lijst van hyperlinks achteraan deze tekst).

Om de inhoud van een of meer tabellen te raadplegen, gebruiken we een SQL-opdracht van het type SELECT. Dergelijke opdrachten produceren altijd een resultaat in tabelvorm (mogelijk een tabel met slechts één of zelfs geen enkele rij). De volgende opdrachten reproduceren de volledige inhoud van de tabel BOOK in de veronderstelling dat zo'n tabel bestaat.

ResultSet resultaattabel;
resultaattabel = opdracht.executeQuery("SELECT * FROM BOOK");

Een resultaattabel bestaat enerzijds uit een tabel met de resultaatrijen van de bevraging, anderzijds uit een wijzer die de positie van de "huidige" rij bijhoudt. Oorspronkelijk staat deze wijzer gepositioneerd vóór de eerste rij; bij iedere aanroep van de methode next() schuift de wijzer één rij naar beneden. De terugkeerwaarde van next() is de logische waarde true totdat de wijzer voorbij de laatste rij wijst. Een typisch programma dat een voor een de rijen van een resultaatset behandelt, ziet er dus als volgt uit:

while (resultaattabel.next()) {
  //... behandel de huidige rij
}

Dat werkt ook als de resultaattabel toevallig leeg is: in dat geval geeft next() reeds de eerste maal false, en wordt het inwendige van de while-lus nooit uitgevoerd.

opeenvolgende aanroepen van next() tot deze de waarde false teruggeeft

Bij de "behandeling" van een afzonderlijke resultaatrij zijn we meestal vooral geïnteresseerd in de inhoud van de individuele cellen. Daartoe staan twee families methoden ter beschikking om diverse gegevenstypes op te halen. De eerste familie neemt een String-parameter met de naam van de gewenste kolom, de tweede familie neemt een int-parameter met het volgnummer van de gewenste kolom. Merkwaardig genoeg worden volgnummers gerekend vanaf 1 (dus niet zoals de elementen van een rij of de karakters van een String).

De methoden getString(String) en getString(int) werken op elk gegevenstype. Daarnaast bestaat een hele verzameling methoden getXXX... voor vele specifieke gegevenstypes, telkens met een passend terugkeertype.

Uitgewerkt voorbeeldprogramma

We geven hieronder een kort maar volledig programma dat interageert met een eenvoudige gegevensbank van het type MS-Access. Wie over een PC met de juiste ODBC-driver beschikt, kan de database downloaden. Maak vervolgens via het configuratiescherm (Start/Instellingen/ConfiguratieScherm...32bit ODBC) een ODBC-gegevensbron ("User Data Source Name") met de naam "KookboekSource" die naar de Access-file verwijst.

De voorbeeld-database hanteert onderstaand gegevensmodel, maar het programma gebruikt alleen de tabel RECEPT.

tabellen RECEPT, RECEPT_BEVAT_INGREDIENT, INGREDIENT
import java.sql.*;

public class TestKookboek {
  public static void main(String[] args) throws Exception {
    Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
    Connection con = DriverManager.getConnection(
      "jdbc:odbc:KookboekSource", "", "");
    Statement stmt = con.createStatement();

    String queryTekst =
      "INSERT INTO RECEPT VALUES (3, 'Spaghetti Bolognese', 4)";
    stmt.executeUpdate(queryTekst);

    queryTekst = "SELECT * FROM RECEPT";
    ResultSet rs = stmt.executeQuery(queryTekst);
    while (rs.next()) {
      int nummer = rs.getInt("NR");
      String omschrijving = rs.getString("OMSCHRIJVING");
      int personen = rs.getInt("PERSONEN");
      System.out.println(nummer + ": " + omschrijving
        + " (" + personen + " personen)");
    }

    queryTekst = "DELETE FROM RECEPT WHERE NR = 3";
    stmt.executeUpdate(queryTekst);

    stmt.close();
    con.close();
  }
}

inhoudstafel en auteursrecht
* STER *

Valid HTML 4.0! Valid CSS!