5.3. JDBC ievads

Ievads

  • JDBC ir abstrakcijas slānis, kurš ļauj lietotājam, neriskējot ar smagām sekām nākotnē, izvēlēties, kuru datubāzi lietot
  • JDBC ļauj rakstīt pieprasījumus konkrētam interfeisam (API)
  • JDBC atbalsta ANSI SQL-2 savietojamas datubāzes, bet to var lietot arī citām datubāzēm

Divi JDBC komponenti

  • Interfeiss, kurš implementējams datubāzu sistēmu veidotājiem (draiveru rakstītājiem)
  • Interfeiss, kuru izmanto Javas aplikāciju rakstnieki

Interfeiss JDBC draveriem

Katram draiverim ir jāimplementē sekojošie interfeisi:

  • java.sql.Connection
  • java.sql.Statement
  • java.sql.PreparedStatement
  • java.sql.CallableStatement
  • java.sql.ResultSet
  • java.sql.Driver

Katram datubāzes draiverim vajag implementēt vēl arī java.sql.Driver interfeisu, kuru izmanto vispārīgā java.sql.DriverManager klase, kad tai jāatrod draiveris konkrētai datubāzei, izmantojot URL sintaksē rakstītu "konekcijas stringu". JDBC ir veidots pēc ODBC (Open Database Connectivity); tādēļ JDBC ar ODBC var viegli sadarboties.

Javas aplikācija un draiveri

Javas aplikācija var piekļūt dažādām datubāzēm ar dažādiem draiveriem; draiveri ir vai nu specializēti priekš JDBC, vai arī tiek izmantots JDBC-ODBC tilts:

Dažādi draiveri

JDBC lietotāja uzdevumi

  • Izveidot JDBC draivera instanci (piemēram, ar konstruktoru vai ar Class.forName(drivername).newInstance() izsaukumu. Vajadzības gadījumā draiveri inicializēt vai reģistrēt.
  • Norādīt datubāzes URLu un, izmantojot to, atvērt konekciju
  • Norādīt komandu jeb pieprasījumu jeb vaicājumu (query)

JDBC plūsma

Javas aplikācijā izveidojas hierarhija ar apmēram šādiem objektiem (no viena augstāka līmeņa objekta var būt atkarīgi vairāki zemāka līmeņa objekti)

  • DriverManager
  • Driver
  • Connection
  • Statement, PreparedStatement, CallableStatement
  • ResultSet
  • DriverManager var strādāt ar vairākiem draiveriem
  • URL stringu padod DriverManager un iegūst konekciju. Vienam un tam pašam draiverim (pat vienai un tai pašai datubāzei var izveidot vairākas konekcijas vienlaikus)
  • Konekcijai var nosūtīt SQL komandas un vaicājumus
  • Komandas (piemēram, INSERT INTO, UPDATE vai DELETE) vienkārši izpildās. Vaicājumi (piemēram, SELECT) atgriež rezultātkopu

Pakotne java.sql

Ar JDBC ir saistīti sekojoši interfeisi

  • java.sql.Driver
  • java.sql.Connection
  • java.sql.Statement
  • java.sql.PreparedStatement
  • java.sql.CallableStatement
  • java.sql.ResultSet
  • java.sql.ResultSetMetaData
  • java.sql.DatabaseMetaData

Datubāzu sistēmas MySQL sagatavošana darbam

  • Atarhivēt MySQL distribūcijas ZIP failu un palaist setup.exe. Izvēlēties kā instalācijas vietu noklusēto direktoriju c:/mysql.
  • Piereģistrēt MySQL serveri kā NT servisu:
    c:/mysql>mysqld-nt --install
    
  • Tiem, kuriem patīk grafisks dialoga interfeiss ar MySQL datubāzi, var izvēlēties kādu rīku (piemēram, MySQL Front). Tomēr ikvienam programmētājam ir derīgi strādāt ar komandrindas rīku c:/mysql/bin/mysql.exe, kurš saprot SQL sintaksē rakstītas komandas.
  • DOS komandrindā piestartējiet mysql.exe un pārliecinieties, ka rodas prompts: mysql>. Ierakstiet tur sekojošas komandas:
CREATE DATABASE demo;
USE demo; 
CREATE TABLE cilveks (id INT, vards CHAR(25), uzvards CHAR(25));
INSERT INTO cilveks (id,vards,uzvards) VALUES (1,"BILL","CLINTON");
INSERT INTO cilveks (id,vards,uzvards) VALUES (1,"AL","GORE");
SELECT * FROM cilveks;
quit

Piekļuvi datubāzei "demo" administrē citā datubāzē "mysql" - izmainot tajā tabulas "db" un "user". Tabulā "user" var pievienot jaunus lietotājus ar parolēm, bet tabulā "db" var uzlikt katram lietotājam atbilstošas privilēģijas.

Ja esat aizmirsuši, kādas datubāzes un tabulas Jūsu MySQL instalācijā ir saliktas, palaidiet citu programmu mysqlshow ar un bez argumentiem. Konkrēti - DOS komandrindā izpildiet šādas komandas, lai pārliecinātos, ka tiešām radusies datubāze "demo" un tajā ir tabula "cilveks".

mysqlshow
mysqlshow demo

JDBC piemērs

import java.sql.*;

public class DBTest {     
    public static void main(String[] args) throws Exception {
        String drivername = "org.gjt.mm.mysql.Driver";
            Class.forName(drivername).newInstance();
            String url = "jdbc:mysql://localhost/demo?user=root";
            Connection con = DriverManager.getConnection(url);
                Statement st = con.createStatement(); 
                ResultSet rs = st.executeQuery("select * from cilveks"); 
                while (rs.next()) {
                        int id = rs.getInt("id"); 
                        String vards = rs.getString("vards"); 
                        String uzvards = rs.getString("uzvards");
                        System.out.println("id = " + id 
                                + ", vards = " + vards
                                + ", uzvards = " + uzvards); 
                }
        }
}

Lai šo piemēru varētu palaist, ir nepieciešams JDBC draiveris priekš MySQL. Ieteicams izmantot "oficiālo" MySQL draiveri "MySQL Connector/J", kuru var dabūt šeit: http://www.mysql.com/downloads/api-jdbc.html. Tas pirms programmas palaišanas jāpievieno savam CLASSPATH:

set CLASSPATH=mysql-connector-java-2.0.14-bin.jar;.
java DBTest

MySQL datubāzes sagatavošana darbam

  • Uzinstaleet MySQL - atpakot un palaist "setup.exe".
  • Palaist "c:\mysql\bin\mysqld.exe" - datubāzes serveri.
  • Palaist turpat "mysql.exe" - datubāzes klientu.
  • Rakstīt šādas komandas:
    use mysql;
    select * from user;
    insert into user values (
        "localhost", "dzonis", PASSWORD("dzonis"), 
        "Y","Y","Y","Y","Y","Y","Y",
        "Y","Y","Y","Y","Y","Y","Y")
    
  • Aizvērt gan "mysqld", gan "mysql".
  • Atvērt "mysqld" (WinNT un Win2000 gadījumā - pārstartēt MySQL servisu) un palaist šādu komandu:
    mysql -u dzonis -p
    Enter Password: dzonis
    
  • Izdot "mysql" promptā šādas komandas:
    create database biblioteka;
    use biblioteka;
    create table gramata (
        id INT, 
        autors CHAR(30), 
        nosaukums CHAR(30), 
        lappuses INT); 
    insert into gramata values (1, "Kaudzites", 
        "Mernieku Laiki", 300);
    ...
    select * from gramata; 
    

Datubāzu sistēmas Oracle sagatavošana darbam

  • Savam Oracle DB adminam jāpalūdz izveidot lietotāja konts jeb shēma. To admins dara apmēram šādi:
    create user
    javatest
    identified by javatest;
    grant connect to
    javatest;
    grant resource to
    javatest;
    
  • Pajautājam Oracle servera "host:port:sid" - mašīnu, porta numuru un Oracle apakšsistēmas identifikatoru. Tas varētu būt, piemēram, šāds:
    olimpus.alise.lv:1521:itndev01
    
  • Ar SQL Plus pieslēdzamies savai shēmai - login lodziņā ir jāievada 3 stringi: "User Name" (shēmas vārds), "Password" (parole, kura sākumā varētu sakrist ar shēmas vārdu), "Host String" (tas pats SIDs, piemēram ITNDEV01).
  • Izveidojam dialogveidā mazu tabuliņu:
    CREATE TABLE cilveks (id INT, vards CHAR(25), uzvards CHAR(25));
    INSERT INTO cilveks (id, vards, uzvards) VALUES (1, 'Bill', 'Clinton');
    SELECT * FROM cilveks;
    
  • Programmā DBTest.java, metodē main() izmainīt pirmās 4 rindiņas. Rakstīt tur apmēram sekojošo:
    String drivername = "oracle.jdbc.OracleDriver";
    Class.forName(drivername).newInstance();
    String url = "jdbc:oracle:thin:@olimpus.alise.lv:1521:itndev01";
    Connection con = DriverManager.getConnection(url, "javatest", "javatest");
    

    Lietotāja vārda un paroles "javatest" vietā Jums būtu jāraksta pašiem savas shēmas vārds un atbilstošā parole.

  • Lai šo programmu varētu palaist, ir jāpievieno savam CLASSPATH mainīgajam draiveris classes14.jar. Vispārīgi runājot, vai lietot "plāno" draiveri vai arī OCI draiveri, ir atkarīgs no aplikācijas īpatnībām - vai svarīgāka ir aplikācijas elastība un viegla piestartēšanās vai arī optimāla ātrdarbība, ko var panākt ar "native" kodu.

JDBC draivera instances izveidošana

Pirms sākt komunicēt ar datubāzi, izmantojot draiveri, šo draiveri vajag ielādēt atmiņā. Sekojoša komanda pieregjistrē datubāzu draiveri draiveru pārvaldniekam (java.sql.DriverManager).

Class.forName("org.gjt.mm.mysql.Driver").newInstance();
new org.gjt.mm.mysql.Driver();

Šis draiveris nav jāpiešķir mainīgajam, bet tas, ja JVM to izvēlēsies, apstrādās visus pieprasījumus, kuri veidojas no DB konekcijām uz atbilstošo datubāzi.

JDBC draiveru ielādēšana

  • Vienlaikus aplikācijas atmiņā var atrasties vairāki draiveri
  • Vairāki no ielādētajiem draiveriem var vienlaikus konektēties pie tās pašas datubāzes
  • Draiveru prioritāti var norādīt sistēmas īpašībā:
jdbc.drivers = com.imaginary.sql.msql.MsqlDriver:Acme.db.driver

Ielādēšana caur jdbc.drivers

JDBC izmanto pirmo atrasto draiveri, pārskatot tos sekojošā secībā:

  • Visus draiverus, kuri norādīti jdbc.drivers sarakstā
  • Visus citus draiverus, kuri ir ielādējušies atmiņā
  • Draiverus, kuru kodu ielādēja (piemēram, dinamiski) no neuzticama avota, izlaižam. Izņēmums ir gadījumos, kad pats konekcijas kods nāk no tā paša avota.

Draivera reģistrēšanās

  • Draiveris statiska koda blokā inicializē savu instanci
  • Tad, kad viņa konstruktors atklāti vai slēpti tiek izsaukts, piereģistrējas pie draiveru pārvaldnieka.

Datubāzes norādīšana

Jānorāda URLs, kurš parasti ir sekojošā izskatā:

jdbc:subprotocol:subname

Piemēram, to iegūst ar šādu piešķiršanu:

String url = new String("jdbc:msql://" + serverName + ":1112/demo"); 

TCP portus klausošamies datubāzēm parasti var noskaidrot noklusēto porta numuru. Bieži vien konekciju stringu sintakse ir sarežģītāka un ir jāmeklē dokumentācijā.

Savienojuma jeb konekcijas atvēršana

Connection con = DriverManager.getConnection(url);
  • Draiveru pārvaldnieks mēģina atrast draiveri, kurš spēj pievienoties norādītajam URLam
  • Ja izveidojas konekcija, tiek atgriezts Connection objekts
  • Connection objekts reprezentē sesiju ar konkrētu datubāzi

Draiveru pārvaldniekā metode getConnection(url) izsauc driver.connection(url) priekš "driver" instancēm, kuras ir ielādētas aplikācijā (augšminētajā prioritāšu secībā). Ja draiveris var pieslēgties datubāzei, tas atgriež "Connection" objektu. Ja draiveris nevar - tas atgriež "null" un draiveru pārvaldnieks ķeras pie nākamā draivera, utt.

Pieprasījuma jeb vaicājuma (query) nosūtīšana

Lai sūtītu pieprasījumus datubāzei, no konekcijas ir jādabū "Statement":

try {
    stmt = con.createStatement();
}
catch (SQLException e) {
    System.out.println(e.getMessage()); 
}
  • Lai nosūtītu INSERT, UPDATE vai DELETE komandu, jālieto stmt.executeUpdate("...") komanda, kura atgriež "int" - komandas iespaidoto rindiņu skaitu
  • Lai nosūtītu SELECT komandu, jālieto stmt.executeQuery("...") komanda, kura atgriež ResultSet - tabulveida objektu tālākai apstrādei

Rezultātu savākšana

  • Vaicājuma rezultāti glabājas kā 0 vai vairāk rindas ResultSet objektā.
  • ResultSet objekts satur "iebūvētu iteratoru", kurš sākotnēji norāda uz nenolasāmu vietu "pirms pirmās rindas"
  • ResultSet.next metode pavirzās uz nākamo rindu; atgriež "false", ja rindu vairs nav. Pirmais "next()" izsaukums padara pirmo rindu par tekošo, otrais - otro rindu, utt.
  • Dažādas "getXxx()" metodes ļauj piekļūt dažādiem laukiem
while (rs.next()) {
    System.out.println("Customer: " + rs.getString(2)); 
        System.out.println("Id: "  + rs.getString(1)); 
        System.out.println(""); 
}

Metodes getXxx()

Klasē ResultSet ir dažādas metodes "getXxx()":

MetodeAtgrieztais tips
getBinaryStream()java.io.InputStream
getBoolean()boolean
getByte()byte
getBytes()byte[]
getDate()java.sql.Date
getDouble()double
getFloat()float
getInt()int
getLong()long
getShort()short
getString()java.lang.String
getTime()java.sql.Time

Sagatavotie pieprasījumi (Prepared Statements)

  • Lai izsauktu to pašu SQL pieprasījumu (iespējams ar dažādiem parametriem), izmantojiet PreparedStatement objektu
  • Preparēts pieprasījums ir ātrdarbīgāks, jo tas ir prekompilēts.
  • Preparētā pieprasījumā ierakstāmos parametrus apzīmē ar (?) simboliem.

Piemērs

Connection con = DriverManager.getConnection(url); 
java.sql.PreparedStatement pstmt = 
    con.prepareStatement
    ("UPDATE table3 SET m = ? WHERE x = ?"); 
// Nododam 2 parametrus. Viens no tiem ciklā mainās
pstmt.setString(1, "Hi"); 
for (int i = 0; i < 10; i++) {
    pstmt.setInt(2,i);
    int j = pstmt.executeUpdate();
    System.out.println(j + " rindas pamainītas, kad i = " + i); 
}

Dažādās setXxx() metodes

MetodeSQL tips
setBinaryStream()LONGVARBINARY
setBoolean()BIT
setByte()TINYINT
setBytes()VARBINARY vai LONGVARBINARY
setDate()DATE
setDouble()DOUBLE
setFloat()FLOAT
setInt()INTEGER
setLong()BIGINT
setShort()SMALLINT
setString()VARCHAR vai LONGVARCHAR
setTime()TIME

Storētās procedūras

String createProcedure = "create procedure SHOW_SUPPLIERS " +
                         "as " +
                         "select SUPPLIERS.SUP_NAME, COFFEES.COF_NAME " +
                         "from SUPPLIERS, COFFEES " +
                         "where SUPPLIERS.SUP_ID = COFFEES.SUP_ID " +
                         "order by SUP_NAME";

(Procedūrām datubāzē var būt arī ieejas un izejas parametri.) Šādu String mainīgajā ierakstītu procedūru var pievienot datubāzei, vienkārši aizsūtot JDBC konekcijai izpildāmu komandu:

Statement stmt = con.createStatement();
stmt.executeUpdate(createProcedure);

Šo procedūru var izpildīt, izmantojot JDBC interfeisus no CallableStatement:

CallableStatement cs = con.prepareCall("{call SHOW_SUPPLIERS}");
ResultSet rs = cs.executeQuery();

Beigās "rs" piepildīsies ar vajadzīgajiem datiem.