Lataa esitys
Esittely latautuu. Ole hyvä ja odota
113
4.3. Tietokantojen ohjelmointirajapinnat
Engl. API (Application Programming Interface). Ohj.kielen täydennys moduuli-/luokkakirjastoilla SQL/CLI (Call-Level Interface), osa SQL-standardia vuodesta 2003. Tunnetuimpia rajapintoja: ODBC (Open DataBase Connectivity): SQL/CLI-pohjainen, Microsoftin kehittämä, yleinen C, C++, C# ja Visual Basic –kielten yhteydessä. JDBC (‘Java DataBase Connectivity’): Sun Micro-systemsin piirissä kehitetty API-kirjasto Javalle. 4-3-OhjRajapinnat Teuhola 2012
114
4-3-OhjRajapinnat Teuhola 2012
JDBC: Yleistä JDBC on Javan luokka-/rajapintakirjastomääritys, joka mahdollistaa SQL-lauseiden suorituksen Javasta käsin (lauseet metodien parametreina). Vastaa dynaamista, sulautettua SQL:ää. Ero SQLJ:hin: JDBC ei ole kielen laajennus, joten standardi Java-kääntäjä riittää. Hyvin tuettu (‘teollisuusstandardi’) Javan etu: siirrettävyys (portability). JDBC:tä käyttävä ohjelma on lähes riippumaton siitä, mikä tietokantajärjestelmä on käytössä (voidaan vaihtaa) 4-3-OhjRajapinnat Teuhola 2012
115
4-3-OhjRajapinnat Teuhola 2012
JDBC-ajuri JDBC-API toimii sovellusohjelmien rajapintana. Yhteydet tietokantaan hoitaa JDBC-ajuri (driver). Kuhunkin tietokantajärjestelmään on oma ajurinsa; saatavana järjestelmän toimittajalta, ks. esim. Ajuri on jar-paketti (esim. postgresql.jar), joka sijoitetaan haluttuun hakemistopolkuun. Polku pitää määritellä CLASSPATH-ympäristömuuttu-jassa. 4-3-OhjRajapinnat Teuhola 2012
116
Asiakas/palvelin-arkkitehtuuri
Tietokannan hallintajärjestelmä JDBC-ajuri Java-ohjelma Tietokanta Asiakas Palvelin 4-3-OhjRajapinnat Teuhola 2012
117
JDBC:n mukaanotto sovellusohjelmaan
JDBC-pakkaukset (kuuluvat J2SE:hen): import java.sql.*; // Perus-JDBC import javax.sql.*; // Laajennuksia, JDBC 2.0, 3.0, 4.0 DriverManager pitää kirjaa ajureista, joita samassa sovelluksessa voi olla useita. Ajuri pitää ladata ja rekisteröidä Driver Managerille esim. seuraavasti: Class.forName(“org.postgresql.Driver”) missä ajurin tiedostopolku on em. jar-paketissa. 4-3-OhjRajapinnat Teuhola 2012
118
Yksinkertaisen JDBC-sovelluksen vaiheet
Ladataan ajuri (vrt. ed.) Muodostetaan yhteys (Connection) tietokantaan Luodaan SQL-lause (Statement) Suoritetaan lause Käsitellään mahdollinen tulosjoukko (ResultSet), tai siepataan ja käsitellään poikkeukset. Toistetaan kohtia 3-5 tarpeen mukaan Suljetaan tietokantaoliot (tulosjoukko, yhteys) 4-3-OhjRajapinnat Teuhola 2012
119
Yhteyden muodostus tietokantaan
Tarvittava metodi löytyy DriverManager-luokasta: DriverManager.getConnection(<url>, <käyttäjä>, <salasana>); missä <url> on muotoa “jdbc:<ajuri>:<datalähde>”, esim. "jdbc:postgresql://kanta.cs.utu.fi:5432/company“ 4-3-OhjRajapinnat Teuhola 2012
120
4-3-OhjRajapinnat Teuhola 2012
SQL-lauseet Lause-olioita (Statement-rajapinta) tarvitaan edustamaan SQL-lauseita. Statement-rajapinnassa on mm. metodeita SQL-lauseen suorittamiseksi. Tärkeä alirajapinta: PreparedStatement: SQL-lause jäsennetään, tarkistetaan ja käännetään vain kerran, mutta suoritetaan ehkä toistuvasti (tehokkaampi). 4-3-OhjRajapinnat Teuhola 2012
121
Statement-olion luonti ja suoritus
Luonti Connection-olion createStatement-metodilla. Suoritus executeQuery- tai executeUpdate-metodilla lauseen tyypistä riippuen. Kyselyn tulos on tyyppiä ResultSet. Esimerkki: Connection c = DriverManager.getConnection(url, user, pwd); Statement st = c.createStatement(); ResultSet res = st.executeQuery( "SELECT Ano, Animi FROM Asiakas;"); 4-3-OhjRajapinnat Teuhola 2012
122
Statement-olion luonti ja suoritus (jatk.)
ResultSet-olio (esimerkissä res) edustaa 2-ulotteista taulukkoa, jota voidaan kulkea iteraattorin tapaan rivi kerrallaan (vrt. SQLJ:n ‘iterator’). Ensin res osoittaa 1. rivin ‘edelle’, josta res.next()-metodilla saadaan 1., 2., … rivi, kunnes res==NULL. Yksittäiset rivin alkiot saadaan res.getxxx(sarake)-metodeilla, missä ‘xxx’ on alkion tyyppi (getInt, getLong, getFloat, getDouble, getString, getBoolean, getDate, getTime, getObject, ym.). ‘Sarake’ voidaan ilmaista joko järjestysnumerolla (1, 2, …) tai tulosattribuutin nimellä. 4-3-OhjRajapinnat Teuhola 2012
123
Statement-olion luonti ja suoritus (jatk.)
JDBC ei tee eroa 1- ja monirivisten tulos-taulukoiden välillä. Jos tuloksessa tiedetään olevan vain 1 rivi, silmukka voidaan välttää, mutta res.next():llä pitää siirtyä 1. riville. Poikkeuksista: JDBC-kutsut voivat nostaa mm. SQLException-poikkeuksia. Ne voidaan joko käsitellä (try … catch …) tai siirtää kutsujalle. 4-3-OhjRajapinnat Teuhola 2012
124
4-3-OhjRajapinnat Teuhola 2012
JDBC-esimerkki // Etsi annetun paikkakunnan henkilöt relaatiosta // Asiakas (Ano, Animi, Paikka). // import java.io.*; import java.sql.*; class HaeAsiakkaat { public static void main(String args[]) throws SQLException, IOException { try { Class.forName (“org.postgresql.Driver”); } catch (ClassNotFoundException cnfe) { System.out.println(“Ajuria ei löydy!”); } // jatkuu seur. sivulla … 4-3-OhjRajapinnat Teuhola 2012
125
JDBC-esimerkki (jatk.)
String tunnus = prompt(“Anna käyttäjätunnus”); // Hoputemetodi String salasana = prompt(“Anna salasana”); Connection conn = DriverManager.getConnection( “jdbc:postgresql://host/tietokanta”, tunnus, salasana); String paikkakunta = prompt(“Anna paikkakunta”); String kys = “SELECT Ano, Animi FROM Asiakas ” “WHERE Paikka = ‘” + paikkakunta + “’;”; Statement stmt = conn.createStatement(); ResultSet res = stmt.executeQuery(kys); while (res.next()) { int no = res.getInt(1); String nimi = res.getString(2); System.out.println(no + “ “ + nimi); } res.close(); conn.close(); } } 4-3-OhjRajapinnat Teuhola 2012
126
JDBC: SQL-lauseen toistuva suoritus
Lause kannattaa jäsentää ja optimoida vain kerran ja käyttää ‘käännettyä’ versiota toistuvissa suorituksissa. PreparedStatement (‘valmisteltu’ lause) on tällainen: perii Statement-rajapinnan, luonti Connection-olion prepareStatement()-metodilla, parametrina SQL-lause. Mutta: Yleensä eri suorituskerroilla on jotain eroa; SQL-lause on voitava parametrisoida. 4-3-OhjRajapinnat Teuhola 2012
127
PreparedStatement: SQL-lauseen parametrisointi
JDBC-ratkaisu: SQL-lauseeseen sijoitetaan tuntemattomien osien kohdalle ‘?’-merkkejä, jotka voidaan eri kerroilla sitoa eri parametriarvoihin. ‘?’-parametrit ajatellaan numeroiduiksi 1, 2, … Parametrisointi rajoitettua; esim. FROM-osan relaationimiä ei voi parametrisoida. Parametrien asetus setxxx()-metodeilla (setInt, setFloat, setString, …) 4-3-OhjRajapinnat Teuhola 2012
128
“Etsi annettujen paikkakuntien asiakkaat”
// … Alkuosa kuten edellä String kys = “SELECT Ano, Animi FROM Asiakas ” “WHERE Paikka = ?;”; PreparedStatement ps = conn.prepareStatement(kys); String paikka = prompt(“Anna paikkakunta, lopuksi tyhjä”); while (!(paikka.equals(“”))) { System.out.println(“Paikkakunnan ” + paikka + “ asiakkaat”); ps.setString(1, paikka); ResultSet res = ps.executeQuery(); while (res.next()) { int no = res.getInt(1); String nimi = res.getString(2); System.out.println(no + “ “ + nimi); } res.close(); paikka = prompt(“Anna paikkakunta, lopuksi tyhjä”); } conn.close(); } } 4-3-OhjRajapinnat Teuhola 2012
129
JDBC: Päivityslauseet
Metodilla executeQuery() voidaan suorittaa vain SELECT-lauseita. Päivityslause suoritetaan käskyllä executeUpdate(), joka palauttaa päivitettyjen rivien määrän. 4-3-OhjRajapinnat Teuhola 2012
130
“Annetun valmistajan tuotteille annetun suuruinen alennus”
// Relaatioskeema: // Tuote (Tno, Tnimi, Valmistaja, Hinta) … String valm = prompt(“Anna valmistaja”); int ale = promptInt(“Anna alennusprosentti”); String paiv = “UPDATE Tuote “ “SET Hinta = Hinta*(100-” + ale + “)/100 “ “WHERE Valmistaja = ‘“ + valm + “’;”); int lkm = conn.executeUpdate(paiv); System.out.println(lkm + “ tuotetta halpeni”); … 4-3-OhjRajapinnat Teuhola 2012
131
Yleistetty ResultSet-rajapinta
Mahdollistaa monipuolisemman vierityksen (scroll): first(), last(), next(), previous(), absolute(int), relative(int) Mahdollistaa nykyistä ResultSet-riviä vastaavan tietokantarivin päivityksen. Vrt. SQLJ: #sql iterator … implements sqlj.runtime.ForUpdate Yleistetty tulosjoukko määritellään Statement-olion luonnin yhteydessä. 4-3-OhjRajapinnat Teuhola 2012
132
Yleistetty ResultSet-rajapinta (jatk.)
Lauseen luonti esim. Statement lause = conn.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); Lauseen suoritus esim.: ResultSet res = lause.executeQuery( "SELECT Ano, Animi, Paikka FROM Asiakas;"); Päivitys, esim. kolmanteen tulosriviin uusi paikka: res.absolute(3); // Siirtyminen 3. riville res.updateString(“Paikka", “Parainen"); // Uusi arvo res.updateRow(); // Päivittää lähderelaation vastinrivin 4-3-OhjRajapinnat Teuhola 2012
133
Esimerkki ResultSet-päivityksestä
// Relaatio: Tuote(Tno, Tnimi, Valmistaja, Hinta) // Laske ‘Vipu Oy:n’ tuotteiden hintoja 5 €:lla. // public static void laskeHintoja(Connection conn) throws Exception { String kys = "SELECT * FROM Tuote WHERE Valmistaja='Vipu Oy‘;"; Statement lause = conn.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet res = lause.executeQuery(kys); while (res.next()) { res.updateInt("hinta", res.getInt("hinta") - 5); res.updateRow(); } res.close(); conn.commit(); conn.close(); 4-3-OhjRajapinnat Teuhola 2012
134
Ehtoja ResultSet-päivitykselle
Tulosrivien alkuperä tietokannassa pitää pystyä automaattisesti ja yksikäsitteisesti selvittämään Päivitys mahdollinen, jos Tulosjoukko on peräisin yhdestä relaatiosta (ei esim. liitoksia) Relaation pääavain on mukana tulosattribuuteissa. Huom! Kyseessä on täsmälleen sama ongelma kuin näkymän (view) päivityksessä. Resultset on itse asiassa eräs näkymä kantaan 4-3-OhjRajapinnat Teuhola 2012
135
JDBC: Mielivaltaiset lauseet
Jos lauseesta ei etukäteen tiedetä, onko se kysely vai päivitys (esim. käyttäjä on syöttänyt sen suorituksen aikana), käytetään execute()-metodia, joka palauttaa boolean-arvon true, jos lause oli kysely, muuten false. Tuloksen poiminta tehdään eri tavoilla tästä riippuen. 4-3-OhjRajapinnat Teuhola 2012
136
Esimerkki: SQL-lauseen lukeminen näppäimistöltä ja suoritus
… String lause = prompt(“Anna SQL-lause”); Statement st = conn.createStatement(); boolean tulos = st.execute(lause); if (tulos) { ResultSet rs = st.getResultSet(); … } else { int lkm = st.getUpdateCount(); … } … 4-3-OhjRajapinnat Teuhola 2012
137
Kyselyn tulokseen liittyvä metadata
Tarvitaan, jos metadata (eli skeemaan liittyvä data) ei ole tiedossa ohjelmaa kirjoitettaessa (vrt. ed. sivun esimerkki). … ResultSet rs = st.getResultSet(); ResultSetMetaData rsmd = rs.getMetaData(); int sarLkm = rsmd.getColumnCount(); for (int sar = 1; sar <= sarLkm; sar++) { System.out.println(“Sarake no “ + sar); System.out.println(“Nimi: “+ rsmd.getColumnName(sar)); System.out.println(“Tyyppi: “ rsmd.getColumnTypeName(sar)); … } 4-3-OhjRajapinnat Teuhola 2012
138
Tietokantayhteyteen liittyvä metadata
Toistasataa erilaista tietoa, esim. tiedot ajurista: … Connection conn = DriverManager.getConnection(…); DatabaseMetaData dbmd = conn.getMetaData(); System.out.println(“Ajuri: “ + dbmd.getDriverName()); System.out.println(“Versio: “ + dbmd.getDriverVersion()); … 4-3-OhjRajapinnat Teuhola 2012
139
Transaktioiden hallinta
Oletusarvoisesti jokainen lause muodostaa oman transaktionsa. Eksplisiittinen hallinta (conn tyyppiä Connection): conn.setAutoCommit(false); … …Päivityslauseita … conn.commit(); // tai conn.rollback(); Voi syntyä SQLException, joka on käsiteltävä. 4-3-OhjRajapinnat Teuhola 2012
140
Ohjelmointitekniikoista
Java-ohjelmoinnissa luokkien suunnittelu on keskeisessä asemassa. Tietokannan käyttö kannattaa keskittää omiin luokkiinsa/metodeihinsa, esim. seuraavasti: Ajuriin liittyvät toiminnot Tietokantayhteyteen liittyvät toiminnot Relaatioita edustavat ‘kuoriluokat’ (‘wrapper’) Relaatioiden rivejä (= tietokannan varsinaisia ‘olioita’) edustavat luokat Varsinaiset sovellukset 4-3-OhjRajapinnat Teuhola 2012
141
Esimerkki apuluokasta: Ajurin ja tietokantayhteyden hallinta
import java.io.*; import java.sql.*; import java.util.*; public class Yhteys { public static Connection luoYhteys(String kanta) throws Exception { String driver = "org.postgresql.Driver"; String url = "jdbc:postgresql://kanta.cs.utu.fi:5432/" + kanta; String user = “ewert"; BufferedReader in = new BufferedReader(new FileReader("sala.txt")); String pwd = in.readLine(); Class.forName(driver); Connection conn = DriverManager.getConnection(url, user, pwd); return conn; } public static void suljeYhteys(Connection conn) throws Exception { conn.close(); } 4-3-OhjRajapinnat Teuhola 2012
142
Esimerkki relaation riviä edustavasta luokasta
// Asiakas-entiteetti import java.util.*; public class Asiakas { private int ano; // Yksityiset private String animi; // esiintymä- private String paikka; // muuttujat public Asiakas(int no, int ni, String pa) { Ano = no; animi = ni; paikka = pa; // Alustaja } public int getAno() { return ano; } // Ns. public String getAnimi() { return animi; } // getter- public String getPaikka() { return paikka; } // metodit public String toString() { return getAno()+”, ’”+getAnimi()+’”, ”’+getPaikka()+”’”; } 4-3-OhjRajapinnat Teuhola 2012
143
Esimerkki relaation ‘kuoriluokasta’
// Asiakas(Ano, Animi, Paikka)-relaatiota edustava luokka import java.util.*; import java.sql.*; public class Asiakkaat { // Uuden asiakkaan lisäys; yhteys oltava luotu public static void uusiAsiakas(Connection conn, Asiakas a) { try { Statement st = conn.createStatement(); String lause = "INSERT INTO Asiakas VALUES(" + a.toString() + “)”; int riveja = st.executeUpdate(lause); conn.commit(); } catch (SQLException se) { System.out.println("Lisäys pieleen!“ + se.toString()); } } 4-3-OhjRajapinnat Teuhola 2012
144
Esimerkki relaation ‘kuoriluokasta’ (jatk.)
// Paikkakunnan asiakkaiden haku Asiakas-relaatiosta (vrt. aik. esim.) // public static ArrayList<Asiakas> haeAs(Connection conn, String pnimi) { ArrayList<Asiakas> arr = new ArrayList<Asiakas>(); try { Statement st = conn.createStatement(); String lause = “SELECT Ano, Animi, Paikka “ + “FROM Asiakas WHERE Paikka =‘”+pnimi + “‘;”; ResultSet res = st.executeQuery(lause); while (res.next()) { Asiakas a = new Asiakas( res.getInt(“Ano”), res.getString(“Animi”), res.getString(“Paikka”)) arr.add(a); } res.close(); } catch (SQLException se) { System.out.println("Paikanhaku ei onnistunut!“ + se.toString()); } return arr; 4-3-OhjRajapinnat Teuhola 2012
145
Esimerkki relaation ‘kuoriluokasta’ (jatk.)
// Asiakkaan paikkakunnan muutos public static void muutaPaikka( Connection conn, Asiakas a, String uusiPaikka) { try { conn.setAutoCommit(false); // Transaktio hallitaan itse Statement st = conn.createStatement(); String lause = “UPDATE Asiakas “ “SET Paikka=‘” + uusiPaikka + “‘” “WHERE Ano=" + a.getAno() + “;”; int riveja = st.executeUpdate(lause); conn.commit(); // Muutoksen vahvistus } catch (SQLException se) { System.out.println("Paikanmuutos epäonnistui:“+se.toString()); try { conn.rollback(); } catch (SQLException re) { // Peruutus System.out.println(“Rollback meni pieleen: “+ re.toString()); } } } 4-3-OhjRajapinnat Teuhola 2012
146
Esimerkki relaation ‘kuoriluokasta’ (jatk.)
// Koko asiakastaulun tulostus ruudulle public static void tulostaKaikki(Connection conn) { try { Statement st = conn.createStatement(); String lause = "SELECT * FROM Asiakas;"; ResultSet res = st.executeQuery(lause); System.out.println(""); System.out.println(" Ano | Animi | Paikka"); System.out.println(" "); while (res.next()) { System.out.print(res.getInt(1)); System.out.print(" | "); System.out.print(res.getString(2)); System.out.print(" | "); System.out.println(res.getString(3)); } res.close(); } catch (SQLException se) { System.out.println( "Asiakkaiden haku ei onnistunut!“ + se.toString()); 4-3-OhjRajapinnat Teuhola 2012
147
Esimerkki kuoriluokan testiohjelmasta
public static void main( String args[ ] ) throws Exception { Connection conn = Yhteys.luoYhteys("til"); // Alustus Asiakkaat.tulostaKaikki(conn); // Rel. sisältö alussa Asiakas a = new Asiakas(444, "Laine", "Naantali”); Asiakkaat.uusiAsiakas(conn, a); // Uuden lisäys Asiakkaat.tulostaKaikki(conn); // Uusi sisältö ArrayList<Asiakas> arr = Asiakkaat.haePaikka(conn, "Turku"); // Kysely System.out.println("Turkulaiset asiakkaat: "); // Vastaus for (int i=0; i<arr.size(); i++) { System.out.println(arr.get(i).toString()); } // Turkulaiset Asiakkaat.muutaPaikka(conn, a, "Raisio"); // Paikkakunnan vaihto Asiakkaat.tulostaKaikki(conn); // Muutettu sisältö Yhteys.suljeYhteys(conn); // Lopetus } 4-3-OhjRajapinnat Teuhola 2012
148
Tietokantasovelluksen laatimisesta
Voidaan käyttää erilaisia ohjelmistoratkaisuja, eli ns. suunnittelumalleja. Yleinen periaate: tietokantaa käsittelevien toimintojen eristäminen omiin luokkiinsa, jolloin niiden ylläpito selkeytyy. Esim. Data Access Objects (DAO) –malli: Kerrosmainen arkkitehtuuri: Käyttöliittymä Sovelluslogiikka Tietokantaa käsittelevät osat sovelluksesta JDBC-rajapinta Tietokanta 4-3-OhjRajapinnat Teuhola 2012
149
Tietokantaa käsittelevien luokkien rakenteellisia vaihtoehtoja
Muodostetaan yksi (iso) luokka, johon kerätään kaikki tietokantaa käsittelevät metodit. Muodostetaan yksi luokka kutakin tietokannan entiteettikokoelmaa (eli relaatiota) kohti, johon kootaan ko. relaatioon liittyvät metodit, mutta mahdollisesti myös esim. liitosoperaatiot muiden relaatioiden kanssa. Muodostetaan yksi luokka kutakin entiteettityyppiä kohti, jonka instanssit vastaavat relaation yksittäisiä rivejä. Yllä tilaus-asiakas-sovelluksessa käytettiin lähinnä kakkosvaihtoehtoa (luokka ’Asiakkaat’). Harjoitustyön malliesimerkissä on piirteitä 1- ja 3-vaihtoehdoista. 4-3-OhjRajapinnat Teuhola 2012
150
Object-relational mapping (ORM)
Relaatiokannan sovellusohjelmointi sisältää runsaasti saman kaavan mukaisia metodeja, jotka muuntavat Javan olioesityksen relaatiomallin mukaiseksi taulukkoesitykseksi ja päinvastoin. Muunnos on mahdollista osittain automatisoida, mikä nopeuttaa sovelluskehitystä ja ylläpitoa. Tarvitaan muunnoksen kuvaus loogisella tasolla. Tämä tehdään usein erillisenä kuvaustiedostona (tyypillisesti XML-kielellä). Ei poista tietokannan suunnittelutarvetta. Esimerkki ORM-tuotteesta: Hibernate 4-3-OhjRajapinnat Teuhola 2012
Samankaltaiset esitykset
© 2024 SlidePlayer.fi Inc.
All rights reserved.