Tietokantaohjelmoinnin tekniikkoja Java-kielellä Ville Kuokkanen Helsinki 6. helmikuuta 2003 Relaatiotietokannat nyt seminaari HELSINGIN YLIOPISTO Tietojenkäsittelytieteen laitos
i Tietokantaohjelmoinnin tekniikkoja Java-kielellä Ville Kuokkanen Relaatiotietokannat nyt seminaari Tietojenkäsittelytieteen laitos Helsingin yliopisto Helsinki 6. helmikuuta 2003, 10 sivua Tämä seminaaripaperi esittelee tietokantaohjelmoinnin tekniikkoja Java-kielellä. Paperissa keskitytään lähinnä relaationalisen tiedon käsittelyyn JDBC-ohjelmointirajapinnan avulla. Aluksi esitellään JDBC-ohjelmointirajapinnan uusin versio 3.0, jonka jälkeen keskitytään kuvaamaan sen tarjoamia tietokantayhteys- ja transaktionhallintaohjelmointitekniikoita. Paperissa on erityisesti huomioitu Java 2 Platform, Enterprise Edition (J2EE) arkkitehtuurin tietokantaohjelmoinnille asettamat vaatimukset.
ii Sisältö 1. JOHDANTO... 1 2. YHTEYDENHALLINTA... 2 2.1 DATASOURCE... 3 2.2 YHTEYSALTAAT... 5 3. TRANSAKTIONHALLINTA... 6 3.1 HAJAUTETUT TRANSAKTIOT... 7 4. JCA JA JDBC... 8 5. YHTEENVETO... 8 LÄHTEET... 9
1 1. Johdanto JDBC API on Java-kielen ohjelmointirajapinta relaationalisen tiedon käsittelyyn. Sen avulla Java-sovellukset voivat kommunikoida yhden tai useamman tietovaraston kanssa. Useimmiten tietovarastona toimii relaatiotietokanta, jonka käsittelyyn käytetään SQLkieltä. [ElH01] JDBC-spesifikaatio (specification) perustuu Open Group (aikaisemmin X/Open) SQL CLI (Call Level Interface) [Ope95] tietokantaohjelmointirajapintaan, joka on myös C- kielisen ODBC-ohjelmointirajapinnan perustana. [ElH01] JDBC API esiteltiin ensimmäisen kerran tammikuussa 1997, jolloin sen tarkoitus oli tarjota keskeiset palvelut relaatiotietokantojen käsittelyyn. Myöhemmin JDBC 2.1 [WhH98] ja JDBC 2.0 Optional package [WhH99] määrittelyt lisäsivät ohjelmointirajapintaan keinot kehittyneempien tietokantaoperaatioiden toteutukseen sekä SQL99 tarjoamien uusien tietotyyppien käsittelyyn. JDBC 2.0 Optional package toi mukanaan myös keinot tietokantojen käsittelyyn sovelluspalvelimien (application server) sovelluskomponenttien (application component) avulla.[sun01] Näiden kehittyneiden määritysten myötä JDBC:stä tuli Java 2 Platform, Enterprise Edition (J2EE) arkkitehtuurin tarjoama peruspalvelu.[sha01a] Viimeisin versio JDBC-spesifikaatiosta on JDBC API 3.0 [ElH01]. Siinä on yhdistetty JDBC 2.1 (java.sql) ja JDBC 2.0 Optional package (javax.sql) määrittelyt sekä liitetty se osaksi Java TM 2 Platform, Standard Edition, version 1.4 (J2SE TM ) alustaa [ElH01]. JDBC 3.0 määrittely on myös osa J2EE version 1.4 alustan (platform) määrittelyä [Sha02]. Versio 3.0 mahdollistaa myös muiden kuin relaatiopohjaisten tietovarastojen käsittelyn. Tällaisia tietovarastoja voivat olla esimerkiksi tiedostojärjestelmät sekä olioperustaiset tietojärjestelmät. SQL99 standardi on olennaisena osana JDBC 3.0 määrittelyä. SQL99 ominaisuuksista on toteutettu ne, jotka tulevat todennäköisesti olemaan laajalti tuettuja seuraavan viiden vuoden aikana (arvio vuodelta 2001). [ElH01]
2 Tässä paperissa tullaan käsittelemään joitakin JDBC:n kehittyneitä ominaisuuksia, kuten tietolähteitä (data source), yhteysaltaita (connection pool), hajautettuja transaktioita (distributed transactions) sekä J2EE-alustan tietovarastoyhteyspalvelun JCA (J2EE Connector architecture) ja JDBC-tietokantayhteyspalvelun yhdistämistä. 2. Yhteydenhallinta Connection-olio edustaa JDBC-ajurin avulla tietovarastoon luotua yhteyttä, jossa tietovarastona voi toimia mikä tahansa tietojärjestelmä, jolle JDBC-ajuri on toteutettu. Sovellusohjelmalla voi olla useita Connection-olioita, jotka voivat viitata yhteen ainoaan tai useaan erilliseen tietovarastoon. [ElH01]. Perinteinen tapa yhteyden luomiseen client-server ympäristössä tapahtuu Driver- Manager luokan metodin DriverManager.getConnection avulla. Esimerkissä 1. on esitetty JDBC-sovelluksen yhteydenluontiprosessi. // Ajurin lataus. Luo instanssin acme.db.driver ajurista. // Tämän jälkeen ajuri on sovellusohjelman saatavilla Class.forName("acme.db.Driver"); // Argumenttien luonti getconnection metodikutsua varten. // Käytetään JDBC-ODBC siltaa yhteyden luontiin String url = "jdbc:odbc:dsn"; String user = "SomeUser"; String passwd = "SomePwd"; // Connection-olion luonti DriverManagerin avulla Connection con = DriverManager.getConnection(url, user, passwd); Esimerkki 1. Ajurin lataus ja Connection-olion luonti DriverManagerin avulla. [ElH01] Toinen tapa yhteyden luomiseen tapahtuu DataSource-rajapintaa käyttämällä. DataSourcen ideana on kätkeä käyttäjältä fyysisen tietokannan sijaintitiedot, jolloin yhteyden luonti tapahtuu hakemalla tietokannalle annetun loogisen nimen avulla DataSourcerajapinnan toteuttava instanssi. Haettu DataSource-instanssi hallitsee kyseisen tietokannan yhteyksien luomisen. JDBC API määrittelee myös kaksi tärkeää DataSourcelaajennusta: yhteysaltaan määrittelevän ConnectionPoolDataSourcen ja sekä hajautettuihin transaktioihin kykenevien yhteyksien luojan XADataSourcen.
3 Seuraavissa aliluvuissa käsitellään DataSource- sekä ConnectionPoolDataSourcerajapintojen käyttöä. XADataSource-rajapintaa käsitellään hajautetun transaktionhallinnan yhteydessä kappaleessa 3. 2.1 DataSource JDBC-spesifikaatio suosittelee käyttämään tietokantayhteyden muodostamiseen DataSource-rajapintaa [ElH01]. DataSourcen tarjoamien yhteyksien muodostamiseen käytetään tietokannan loogista nimeä, jolloin alla olevan fyysinen tietokannan tiedot piilotetaan asiakasohjelmilta. Tällöin saavutetaan se etu, että fyysisen tietokannan sijainti tai koko tietojärjestelmä voidaan haluttaessa vaihtaa. DataSource-rajapinta voidaan toteuttaa, joko JDBC-ajurissa itsessään tai sitten sovelluspalvelin tarjoaa palvelun, jonka avulla JDBC-ajuri voidaan konfiguroida osaksi sovelluspalvelimen yhdistyvyyspalveluita. Jotta DataSource-instanssia voitaisiin käyttää sovellusohjelmassa, tulee sen looginen nimi liittää osaksi nimipalvelua [ElH01]. Javan nimipalvelu on nimeltään JNDI (Java Naming and Directory Interface ) [JNDI99]. DataSource-rajapinta voidaan toteuttaa siten, että se läpinäkyvästi toteuttaa yhteyksien varastoimisen (connection pooling, ConnectionPoolDataSource) tai sen tarjoamat yhteydet ovat hajautettuihin transaktioihin kykeneviä (XADataSource). Sovellusohjelmien yhteyksien nouto tapahtuu myös näissäkin tapauksissa DataSource-rajapintaa käyttämällä. JDBC-spesifikaatio määrittelee joukon ominaisuuksia (properties), joita käytetään identifioimaan ja kuvaamaan DataSource-toteutusta [ElH01]. Ominaisuuksien määrä riippuu siitä onko kyseessä DataSource-, ConnectionPoolDataSource- vai XADataSourcerajapinnan olio. Kaikkien Datasource-toteutusten tulee kuitenkin toteuttaa descriptionominaisuus. Taulukossa 1. on esitetty DataSourcen standardiominaisuudet.
4 Property Name Type Description databasename String name of a particular database on a server datasourcename String a data source name; used to name an underlying XADataSource object or ConnectionPoolDataSource object when pooling of connections is done description String description of this data source networkprotocol String network protocol used to communicate with the server password String a database password portnumber int port number where a server is listening for requests rolename String the initial SQL rolename servername String database server name user String user s account name Taulukko 1. DataSourcen standardiominaisuudet [ElH01]. JDBC-sovellusohjelmat eivät käsittele suoraan DataSource-ominaisuuksia vaan ne asetetaan sovelluspalvelimen hallintatyökaluilla. DataSource-olion liittäminen sovelluspalvelimen nimiavaruuteen tapahtuu JNDI-nimipalvelun avulla. Esimerkissä 2. esitetään kuinka JNDI-nimipalvelun avulla voidaan VendorDataSource-olio ottaa käyttöön (deploy) J2EE-sovelluspalvelimessa. // Luodaan VendorDataSource olio ja asetetaan sen ominaisuudet VendorDataSource vds = new VendorDataSource(); vds.setservername("my_database_server"); vds.setdatabasename("my_database"); vds.setdescription("data source for inventory and personnel"); // Käyeteään JNDI:tä rekisteröimään uusi VendorDataSource olio. // Haetaan JNDI-juurikonteksti ja sidotaan looginen nimi // "jdbc/acmedb" uuteen luotuun VendorDataSource olioon. Context ctx = new InitialContext(); ctx.bind("jdbc/acmedb", vds); Esimerkki 2. VendorDataSource-olion käyttöönotto sovelluspalvelimessa. [ElH01] Tämän jälkeen jdbc/acmedb-niminen DataSource on sovellusohjelmien käytössä. Esimerkissä 3. esitetään kuinka sovellusohjelma voi JNDI-nimipalvelun avulla hakea yhteyden jdbc/acmedb tietokantaan. // Haetaan initial context Context ctx = new InitialContext(); // Haetaan "jdbc/acmedb" loogista nimeä vastaava DataSource-olio ja // käytetään sitä yhteyksien luontiin. DataSource ds = (DataSource)ctx.lookup("jdbc/AcmeDB"); Connection con = ds.getconnection("user", "pwd"); // tässä välissä suoritetaan normaaleja JDBC-käskyjä // yhteyden sulkeminen
5 con.close(); Esimerkki 3. Yhteyden luominen DataSourcen avulla [ElH01] 2.2 Yhteysaltaat Tavallisessa DataSource toteutuksessa sovelluksen Connection-olion ja fyysisen tietokantayhteyden välillä on 1:1 vastaavuus [ElH01]. Tämä tarkoittaa sitä, että sovellusohjelman Connection-olio suljettaessa myös fyysinen tietokantayhteys lakkaa olemasta. Fyysisen tietokantayhteyden luominen sekä sulkeminen ovat usein raskaita operaatioita. Erityisesti J2EE-sovellusympäristössä, jossa client-istunnot (client session) ovat usein hyvinkin lyhyitä, uuden yhteyden luominen vaikuttaa tehokkuuteen ratkaisevasti. Esimerkiksi jokainen tilattoman Session-pavun (Stateless Session Bean) metodi voi luoda ja sulkea uuden Connection-olion. Ratkaisu tähän on yhteyksien varastoiminen yhteysaltaiden (connection pool) avulla [ElH01, Sha01a]. Yhteysaltaat varastoivat fyysisiä tietokantayhteyksiä välimuistissa, josta niitä jaetaan sovellusohjelmien käyttöön. Näin säästytään turhilta ja aikaa vievältä yhteyksien avaamisilta ja sulkemisilta. Yhteysallas voidaan toteuttaa joko JDBC-ajurissa ConnectionPoolDataSourcerajapinnan avulla, jota sovelluspalvelin käyttää yhteysaltaan luonnissa ja hallinnassa tai sovelluspalvelin voi tarjota DataSource-wrapperin, joka toteuttaa yhteyksien varastoinnin. JDBC 3.0-spesifikaatio esittelee uutena ominaisuutena myös valmisteltujen käskyjen varastoinnin ( prepared statement pooling). Tällöin yhteyksien varastoinnin lisäksi varastoidaan myös PreparedStatement-oliota [ElH01]. JDBC API määrittää joukon ominaisuuksia, joita voidaan käyttää ConnectionPoolDataSource-toteutuksen yhteyksien varastoinnin hallitsemiseen [ElH01]. Taulukossa 2. on esitetty ConnectionPoolDataSourcen standardiominaisuudet.
6 Property Name Type Description maxstatements int The total number of statements that the pool should keep open. 0 (zero) indicates that caching of statements is disabled. initialpoolsize int The number of physical connections the pool should contain when it is created minpoolsize int The number of physical connections the pool should keep available at all times. 0 (zero) indicates that connections should be created as needed. maxpoolsize int The maximum number of physical connections that the pool should contain. 0 (zero) indicates no maximum size. maxidletime int The number of seconds that a physical connection should remain unused in the pool before the connection is closed. 0 (zero) indicates no limit. propertycycle int The interval, in seconds, that the pool should wait before enforcing the current policy defined by the values of the above connection pool properties Taulukko 2. ConnectionPoolDataSourcen standardit ominaisuudet [ElH01]. Yhteyksien ja valmisteltujen käskyjen varastointi ei näy suoraan sovellusohjelmalle, vaan se käyttää normaaliin tapaan DataSource-rajapinnan käskyjä yhteyksien noutamiseen kuten Esimerkissä 3. Erona on se, että ohjelman suorittaessa komennon con.close() fyysistä tietokantayhteyttä ei suljetakkaan, vaan Connection-olio palaa takaisin yhteysaltaaseen, josta se voidaan luovuttaa jonkun toisen sovellusohjelman käyttöön. 3. Transaktionhallinta JDBC version 3.0 transaktionhallinta pohjautuu SQL99-spesifikaatioon ja se on suunniteltu yhteensopivaksi Java Transaction API (JTA) määrityksen kanssa [ChM99]. JDBC:n transaktiohallinta sisältää kolme käsitettä [ElH01]: Auto-commit toiminto (auto-commit mode) Transaktion eristyneisyystasot (transaction isolation levels) Jatkoaloituskohta (Savepoints) Auto-commit toiminolla määritellään transaktioiden automaattinen rajaus. Transaktion eristyneisyystasoilla määritellään transaktion eristyneisyys muista samaan aikaan suoritettavista transaktiosta. Jatkoaloituskohta on JDBC 3.0 määritelty uusi ominaisuus transaktion hienorakenteisempaan suoritukseen. Jokaisen JDBC yhteensopivan (JDBC compliant) ajurin pitää tukea transaktioita [ElH01].
7 Nämä säännöt pätevät yhden Connection-olion suorittamaan transaktioon kuten myös useita Connection-olioita sisältävään hajautettuun transaktioon. Seuraavassa aliluvussa käsitellään hajautettujen transaktioiden toteutusta JDBC:llä. 3.1 Hajautetut transaktiot Hajautetussa transaktiossa on kyse transaktiosta, joka sisältää useita tietokantayhteyksiä yhteen tai useampaan tietovarastoon. Hajautettujen transaktioiden ympäristö koostuu seuraavista osista [ElH01] : Tapahtumankäsittelijä (transaction manager) hallitsee globaalin transaktion suoritusta. Käyttää tähän kaksi-vaihe commitointia (two-phase commit). Tapahtumankäsittelijä on tyypillisesti JTA-toteutus sovelluspalvelimessa. XADataSource, XAConnection ja XAResource rajapinnat toteuttava JDBC-ajuri. Sovellukselle näkyvä DataSource-toteutus, joka toimii liittymänä XADataSource-olioon ja hoitaa yhteydet tapahtumankäsittelijään. Sovelluspalvelin toteuttaa yleensä tämän. Resurssinkäsittelijä (recource manager) hoitaa varsinaisen alla olevan tiedon hallinnan. JDBC:ssä resurssinkäsittelijöinä ovat tietokannan hallintajärjestelmät. Useimmiten hajautettujen transaktioiden ympäristönä toimii kolmitasoiseen arkkitehtuuriin perustuva järjestelmä, joka koostuu asiakaskerroksesta (client tier), välikerroksesta (middle tier) ja tietovarastokerroksesta (EIS tier). Asiakaskerroksen muodostavat järjestelmän asiakassovellukset. Välikerroksena toimii yleensä J2EE-palvelin, joka hoitaa globaalin transaktion hallinnan. Tietovarastokerroksena ovat järjestelmän relaatiotietokannat, joille löytyy hajautettuihin transaktioihin kykenevät JDBC-ajurit. Esimerkiksi seuraavassa tarvitaan hajautettua transaktiota. Sovellusohjelma kutsuu EJBpavun A metodia, joka päivittää tietokantaa X. Tämän jälkeen EJB-papu A kutsuu toisen EJB-pavun B metodia, joka lähettää JMS-viestin päivityksen toteutuksesta käyttäen JMS-sanomanvälityspalvelua. Tällöin J2EE-palvelimen tapahtumankäsittelijä huolehtii siitä, että sekä tietokantapäivitys että JMS-sanomalähetys tulevat suoritettua ja vahvistettua (commit) tai sitten molemmat tapahtumat perutaan (rollback).
8 4. JCA ja JDBC J2EE Connector Architecture 1.0 (JCA) spesifikaatio määrittelee yleiset säännöt resurssisovittimista (resource adaptor) J2EE-alustalla [Sha01b]. Resurssisovitin toimii J2EEympäristössä ulkoisen järjestelmän ja sovelluspalvelimen standardina yhdistämiskeinona. JCA määrittelee myös resurssisovittimen pakkausmuodon, jonka avulla resurssi voidaan ottaa käyttöön (deploy) J2EE-palvelimissa. JCA-määrittelyn mukaisen resurssisovittimen toteuttamiseen JDBC-ajurille on olemassa useita vaihtoehtoja [ElH01]: Kirjoitetaan JDBC-ajurille joukko luokkia, jotka toteuttavat JCAmäärityksen säännöt. Toteutetaan järjestelmälle resurssisovitin sellaisenaan. Sun on esitellyt hiljattain myös JDBC TM Connector-tekniikan, jonka avulla JDBC-ajuri voidaan muuntaa JCA-standardin mukaisiksi resurssisovittimeksi [Sun02]. JDBC Connector- tekniikan suurin etu on siinä, ettei JDBC-ajurin koodia tarvitse ollenkaan muuttaa. 5. Yhteenveto Tässä seminaaripaperissa on käsitelty tietokantaohjelmoinnin tekniikkoja Java-kielellä. Paperissa on pääasiassa keskitytty esittelemään JDBC version 3.0 tarjoamia tietokantayhteyspalveluja sekä hajautettujen transaktioiden hallintaa. Lisäksi on esitelty JDBCtietokantayhteyspalvelun muuntaminen J2EE-alustan JCA-standardin mukaiseksi tietovarastoyhteyspalveluksi. JDBC on saavuttanut vankan aseman relaatiotietokantaohjelmoinnissa. Erityisesti olemassa olevien yritystietojärjestelmien integrointi osaksi yritysten WWWliiketoimintapalveluita on vauhdittanut JDBC-tekniikan kehitystä.
9 Lähteet ChM99 Cheung S., Matena V., Java Transaction API (JTA) Specification, Version 1.0.1, Final Release, Sun Microsystems, April 29, 1999. http://java.sun.com/products/jta/index.html [6.2.2003] ElH01 Ellis J., Ho L., JDBC 3.0 Specification, Final Release, Sun Microsystems, December 1, 2001. http://java.sun.com/products/jdbc/download.html [6.2.2003] JNDI99 Java Naming and Directory Interface (JNDI) API Specification 1.2, Sun Microsystems, July 14, 1999. http://java.sun.com/products/jndi/1.2/javadoc/ [6.2.2003] Ope95 Data Management: SQL Call Level Interface (CLI), Open Group Technical Standard, The Open Group, March 1995. http://www.opengroup.org/products/publications/catalog/c451.htm [6.2.2003] Sha01a Shannon B., Java 2 Platform, Enterprise Edition Specification Version 1.3, Final Release, Sun Microsystems, August 22, 2001. http://java.sun.com/j2ee/download.html [6.2.2003] Sha01b Sharma R., J2EE Connectors Specification, Version 1.0, Final Release, Sun Microsystems, August 22, 2001. http://java.sun.com/j2ee/connector/download.html [6.2.2003] Sha02 Shannon B., Java 2 Platform, Enterprise Edition Specification Version 1.4, Proposed Final Draft 2, Sun Microsystems, November 12, 2002. http://java.sun.com/j2ee/download.html [6.2.2003] Sun01 JDBC Technology Guide: Getting Started, Sun Microsystems, 2001. http://java.sun.com/j2se/1.4/docs/guide/jdbc/getstart/gettingstartedtoc.fm.html [6.2.2003] Sun02 JDBC TM Connector 1.0 Early Access Release, Sun Microsystems, 2001. http://developer.java.sun.com/developer/earlyaccess/jdbc/index.html [6.2.2003]
10 WhH98 White S., Hapner M., JDBC 2.0 Standard Extension API Specification, Version 1.0, Final Release, Sun Microsystems, December 7, 1998. http://java.sun.com/products/jdbc/download.html [6.2.2003] WhH99 White S., Hapner M., JDBC 2.1 API Specification, Version 1.1, Final Release, Sun Microsystems, October 5, 1999. http://java.sun.com/products/jdbc/download.html [6.2.2003]