Java: Föreläsning 6

Av Thomas Padron-McCarthy (Thomas.Padron-McCarthy@tech.oru.se). Senaste ändring 17 november 2003.

Ungefär motsvarande föreläsningsanteckningar från förra året: Delar av java013.pdf

Innehåll i föreläsning 6

JDBC

Se Eriksson kapitel 15: Databasprogrammering med JDBC. (Ett exempel på att Erikssons bok tar upp mer än de flesta andra Java-böcker.)

Relationsdatabaser och SQL

En databas är en samling data, och hanteras av en databashanterare. En relationsdatabas består av en eller flera tabeller, var och en med med noll eller flera rader och en eller flera kolumner. Varje tabell har ett namn. Varje kolumn har ett namn.

Person

Number Name Telefon
1 Olle 260088
2 Stina 282677
3 Saddam 260088
4 Lotta 174590

Enkel SQL-fråga:

select number, name from person where number < 4
Resultat:

Person

Number Name
1 Olle
2 Stina
3 Saddam

Själva tabellen skapas med ett SQL-kommando:

create table Person
    (number integer,
    name varchar(6),
    telefon varchar(6),
    primary key (number));
Att lägga till en rad:
insert into person values (7, 'Klas', '260088');
Att ändra på en rad:
update person set telefon = '20270' where number = 7;

JDBC-drivrutin

Vi använder en databashanterare som heter Mimer, som kör på en maskin som heter basen.oru.se. Ladda hem Mimers JDBC-drivrutinen från www.mimer.se, eller direkt här. Spara filen som mimjdbc2.jar.

Lägg in själva filen mimjdbc2.jar i CLASSPATH. Linux-exempel:

setenv CLASSPATH $CLASSPATH':'/home/padrone/tmp/fo-06/mimjdbc2.jar

JDBC: Uppkoppling och enkel SQL-fråga

Filen PersonTest.java:
import java.sql.*;

public class PersonTest {
    public static void main(String args[]) {
        try {
            Class.forName("com.mimer.jdbc.Driver");
            String url = "jdbc:mimer://bengt:soptipp@basen.oru.se/bilbasen";
            Connection con = DriverManager.getConnection(url);
            Statement stmt = con.createStatement();
            String sql = "select number, name from person where number < 4";
            ResultSet rs = stmt.executeQuery(sql);
            while (rs.next()) {
                int number = rs.getInt(1);
                String name = rs.getString(2);
                System.out.println("Person nummer " + number + " heter " + name + ".");
            }
            rs.close();
            stmt.close();
            con.close();
        }
        catch (SQLException e) {
            while (e != null) {
                System.out.println("SQLException:");
                System.out.println("    SQLState: " + e.getSQLState());
                System.out.println("    Message: " + e.getMessage());
                System.out.println("    ErrorCode: " + e.getErrorCode());
                e = e.getNextException();
            }
        } catch (Exception e) {
            System.out.println("Någon annan exception:");
            e.printStackTrace();
        }
    } // main
} // class PersonTest
Körexempel:
Person nummer 1 heter Olle.
Person nummer 2 heter Stina.
Person nummer 3 heter Saddam.
Körexempel med felaktigt lösenord:
SQLException:
    SQLState: 28000
    Message: Login failure
    ErrorCode: -14006
Körexempel med felstavad SQL-fråga (nummer i stället för number):
SQLException:
    SQLState: 42000
    Message: NUMMER is not a column of an inserted table, updated table or any table identified in a FROM clause
    ErrorCode: -12202

ODBC

Jämför med ODBC i C eller C++: odbc-test-01.c. (Och då ingår inte laddning av drivrutinen och angivande av datakälla, utan det får man göra i kontrollpanelen.)

ODBC:

Uppdatering med JDBC

Filen NewPerson.java:
import java.sql.*;
import java.io.*;

public class NewPerson {
    public static void main(String args[]) {
        BufferedReader reader
            = new BufferedReader(new InputStreamReader(System.in));
        String numberString;
        int number;
        String name;
        String phone;
        try {
            System.out.println("Mata in en ny person.");
            System.out.print("Vad är personens nummer? ");
            numberString = reader.readLine();
            number = Integer.parseInt(numberString);
            System.out.print("Vad är personens namn? ");
            name = reader.readLine();
            System.out.print("Vad är personens telefonnummer? ");
            phone = reader.readLine();
        }
        catch (IOException e) {
            System.out.println("Kunde inte läsa!");
            return;
        }
        catch (NumberFormatException e) {
            System.out.println("Inte ett korrekt tal.");
            return;
        }

        try {
            Class.forName("com.mimer.jdbc.Driver");
            String url = "jdbc:mimer://bengt:soptipp@basen.oru.se/bilbasen";
            Connection con = DriverManager.getConnection(url);
            Statement stmt = con.createStatement();
            String sql = "insert into person values (" + number + ", '" + name + "', '" + phone + "')";
            System.out.println("SQL-kommandot: " + sql);
            int rowCount = stmt.executeUpdate(sql);
            System.out.println(rowCount + " rader ändrade.");
            stmt.close();
            con.close();
        }
        catch (SQLException e) {
            while (e != null) {
                System.out.println("SQLException:");
                System.out.println("    SQLState: " + e.getSQLState());
                System.out.println("    Message: " + e.getMessage());
                System.out.println("    ErrorCode: " + e.getErrorCode());
                e = e.getNextException();
            }
        } catch (Exception e) {
            System.out.println("Någon annan exception:");
            e.printStackTrace();
        }
        System.exit(0);
    } // main
} // class NewPerson
Körexempel 1:
Mata in en ny person.
Vad är personens nummer? 7
Vad är personens namn? Klas
Vad är personens telefonnummer? 260088
SQL-kommandot: insert into person values (7, 'Klas', '260088')
1 rader ändrade.
Körexempel 2:
Mata in en ny person.
Vad är personens nummer? 1
Vad är personens namn? Hanna
Vad är personens telefonnummer? 767610
SQL-kommandot: insert into person values (1, 'Hanna', '767610')
SQLException:
    SQLState: 23000
    Message: PRIMARY KEY constraint violation, attempt to insert duplicate value
    ErrorCode: -10101

JDBC i en applet

Filen MimerApplet.java:
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.sql.*;

public class MimerApplet extends JApplet implements ActionListener {
    public void actionPerformed (ActionEvent event) {
        try {
            Class.forName("com.mimer.jdbc.Driver");
            String url = "jdbc:mimer://bengt:soptipp@basen.oru.se/bilbasen";
            Connection con = DriverManager.getConnection(url);
            Statement stmt = con.createStatement();
            String sql = "select number, name from person where number < 4";
            ResultSet rs = stmt.executeQuery(sql);
            text.setText("Personer:\n");
            while (rs.next()) {
                int number = rs.getInt(1);
                String name = rs.getString(2);
                text.append("Person nummer " + number + " heter " + name + ".\n");
            }
            rs.close();
            stmt.close();
            con.close();
        }
        catch (SQLException sqle) {
            while (sqle != null) {
                text.setText("SQLException:\n");
                text.append("    SQLState: " + sqle.getSQLState() + "\n");
                text.append("    Message: " + sqle.getMessage() + "\n");
                text.append("    ErrorCode: " + sqle.getErrorCode() + "\n");
                sqle = sqle.getNextException();
            }
        } catch (Exception exc) {
            text.setText("Other Exception:\n");
            text.append(exc.toString() + "\n");
        }
    } // actionPerformed

    private JTextArea text;

    public void init() {
        Container cp = getContentPane();
        cp.setLayout(new FlowLayout());
        cp.add(new JLabel("En applet som hämtar data om personer"));
        text = new JTextArea("Här kommer personerna att skrivas ut", 4, 30);
        cp.add(new JScrollPane(text));
        JButton runButton = new JButton("Hämta data");
        runButton.addActionListener(this);
        cp.add(runButton);
    } // init

    public static void main(String[] args) {
        MimerApplet applet = new MimerApplet();
        JFrame frame = new JFrame("MimerApplet");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(applet);
        frame.setSize(400, 200);
        applet.init();
        applet.start();
        frame.setVisible(true);
    } // main
} // class MimerApplet
(main-metoden är bara med som ett trick, så man kan köra appleten som en applikation.)

Appleten (i Mozilla) före SQL-frågan:

Appleten i Mozilla, före SQL-frågan
Appleten efter SQL-frågan:
Appleten i Mozilla, efter SQL-frågan
Ovanstående är när databasservern och appleten finns på samma dator. Annars:
Säkerhetsstopp i appleten
Hela felmeddelandet:
SQLException:
    SQLState: 08001
    Message: Could not establish connection to server bilbasen on host basen.oru.se using port 1360, java.security.AccessControlException: access denied (java.net.SocketPermission basen.oru.se resolve)
    ErrorCode: -22017
Av säkerhetsskäl får en applet bara göra nätverksuppkopplingar mot samma dator som den själv kommer ifrån.

Några standardmetoder

Metoder som är definierade i rotklassen Object, men kan omdefinieras: Metoder som måste definieras om man implementerar gränssnittet Comparable: Klassen Telefonnummer, ur filen Datatest.java från föreläsning 5:
class Telefonnummer implements Comparable {
    private int landskod, riktnummer, abonnentnummer;

    public Telefonnummer(int landskod, int riktnummer, int abonnentnummer) {
        this.riktnummer = riktnummer;
        this.abonnentnummer = abonnentnummer;
        this.landskod = landskod;
    }
    public Telefonnummer(int riktnummer, int abonnentnummer) {
        this(46, riktnummer, abonnentnummer);
    }
    public Telefonnummer(int abonnentnummer) {
        this(46, 19, abonnentnummer);
    }

    public String toString() {
        return "+" + landskod + "-(0)" + riktnummer + "-" + abonnentnummer;
    }

    public int compareTo(Object o) {
        Telefonnummer rhs = (Telefonnummer)o;
        if (this.landskod < rhs.landskod)
            return -1;
        if (this.landskod > rhs.landskod)
            return 1;
        if (this.riktnummer < rhs.riktnummer)
            return -1;
        if (this.riktnummer > rhs.riktnummer)
            return 1;
        if (this.abonnentnummer < rhs.abonnentnummer)
            return -1;
        if (this.abonnentnummer > rhs.abonnentnummer)
            return 1;
        return 0;
    }

    public boolean equals(Object o) {
        if (!(o instanceof Telefonnummer))
            return false;
        Telefonnummer rhs = (Telefonnummer)o;
        return this.landskod == rhs.landskod &&
            this.riktnummer == rhs.riktnummer &&
            this.abonnentnummer == rhs.abonnentnummer;
    }

    public int hashCode() {
        int result = 17;
        result = 37 * result + landskod;
        result = 37 * result + riktnummer;
        result = 37 * result + abonnentnummer;
        return result;
    }
} // class Telefonnummer
Notera att två telefonnummer är lika med varandra (med equals) om de är likadana, inte bara om de är samma objekt. Mer om detta i föreläsning 7.