Tietokannan käyttö ja GXT (Google Web Toolkit + EXT)

Tekijä: Marko Seppänen
Teostyyppi: harjoitustyö
Oppilaitos: Saimaan ammattikorkeakoulu
Julkaisuajankohta: Joulukuu 2009
Kurssi: Tietokantaohjelmointi

 

 

1 Johdanto

2 Google Web Toolkitistä

3 Ext GWT:stä

4 Google Web Toolkitin RPC (harjoitus 1)

5 Vastaussanomien tietorakenteita tietokantakyselyissä (harjoitus 2)

5.1 Käyttötapauskaavio

5.2 Vastaussanoman tietorakenteena serialisoitu luokka

5.3 Vastaussanoman tietorakenteena HTML-koodia String-kääreessä

5.4 Vastaussanoman tietorakenteena XML, Grid-komponentille

6 Otteita tietokannan täyttöskriptistä

7 JDBC

7.1 Vaiheet tietokannan käyttämiseksi Java-ohjelmassa

7.2 JDBC-ajureita

8 Termiluettelo

 

 

Johdanto


Tarkoituksenani oli laajentaa ja syventää Java-osaamistani toteuttamalla Google Web Toolkitillä ja Ext Gwt:llä prototyyppimainen sovellus, jonka toteuttamisessa joutuisin käsittelemään erilaisia tietorakenteita, tietokannasta saatujen vastausten transformointia, sekä käyttöliittymäkomponentteja. Tämä on myös amk:n Tietokantaohjelmointi-kurssin korvaava harjoitustyö. Kurssilla ohjelmointikielenä olisi ollut C# ja ohjelmointiympäristönä Visual Studio, mutta valitsin mieluummin jatkavani parempaan vauhtiin päässyttä Java/Eclipse -kombinaation käyttöä.

Näin tässä myös hyvän tilaisuuden luoda mentaalisia yhteyksiä muihin käymiini kursseihin kanssa mm. UML-kaavioiden (Ohjelmiston määrittely ja suunnittelu), RPC:n (Hajautetut sovellukset), suunnittelumallit (Olio-ohjelmoinnin suunnittelumallit) osalta. Käytetty tietokanta oli alun perin tarkoitus tehdä uusiksi noudattaen hyvää suunnittelutapaa eli käsiteanalyysiä ja ER-kaavioita, mutta nämä tulivat paremmin käsitellyiksi toisessa yhteydessä, joten päätin jättää tämän prototyyppisovelluksen osalta tietokannan nykyisenlaiseksi. Sovellus itsessään on syntynyt kahteen mainitun ohjelmistokehyksen ominaisuuksiin tutustumisen tahdissa, kertaalleen rönsyjä karsien.

Kehityksen vaiheista on kolme videota Vimeossa:

·        http://vimeo.com/7108775 (vaihe 3)

·        http://vimeo.com/6955869 (vaihe 2)

·        http://vimeo.com/6709667 (vaihe 1)

 

Google Web ToolkitistÄ


Google Web Toolkit (GWT) on ohjelmistokehityspaketti selainpohjaisten sovelluksien rakentamiseen.

Googlen Web Toolkit mahdollistaa websovellusten kirjoittamisen Java-kielellä, ilman että sovelluskehittäjän tarvitsee tietää Javascriptistä yhtään mitään. GWT kääntää asiakaspuolen koodin sellaiseksi optimoiduksi Javascript-koodiksi, että sovellus toimii kaikissa merkittävimmissä selaimissa yhtäläisesti. Henkilölle, jonka ohjelmointikielien oppiminen on mennyt järjestyksessä ”ensin Javascript, HTML, PHP ja sitten vasta Java” tässä on jotain fantastista.

Google Web Toolkit, kuten myös Ext GWT käyttävät etäkutsuihin (sovelluksen selainpuolen osa, asiakas,  lähettää kutsun palvelinosalle) RPC:tä (en. remote procedure call). RPC on yleisnimike, joka voidaan toteuttaa usealla eri tavalla. GWT  tarjoaa mekanismin, joka sisältää tehokkaan ajonaikaisen asiakas- ja palvelinpuolen koodin generoinnin siirrettävien objektien serialisointiin ja käännöksen Javasta Javascriptiksi. GWT ei kuitenkaan sido ohjelmoijaa jonkin tietyn RPC mekanismin käyttäjäksi, sillä tarjolla on RequestBuilder-luokka omien siirtotemekanismien rakentamiseen, sekä mahdollisuus kolmannen osapuolen kehittelemien kirjastojen käyttöön.

Ajonaikainen muunnos Javasta Javascriptiksi yksinkertaisesti vain toimii, sitä ei missään vaiheessa joudu erityisesti ajatelleeksikaan, että koodi mitä selainpuolella ajetaan, on Javascriptiä. Myös olioiden, primitiivityyppien, sekä erilaisten tietorakenteiden ja mallien siirto palvelimelta selaimelle, ja toisinpäin, sujuu näppärästi serialisointia hyödyntäen; Javalla toteutetut oliot ja muut muuntuvat lennossa Javascriptin vastaaviksi.

GWT-sovelluksia ajetaan webselaimessa, mutta kutsut, viestit ja käskyt, joita sovellus lähettää sovelluksen palvelinosalle, eivät aiheuta websivun uudelleen lataamista, vaan Ajax-hengessä sovellus käyttää taustalla Javascriptin XMLHttpRequest-kutsuja, jotka päivittävät sovelluksesta/sivusta vain tarpeellisen osan tai hakevat palvelimelta käynnissä olevan metodin tarvitsemaa lisädataa.

Javascript asettaa tietyt rajoitukset sille mitä kaikkea voidaan tai on haluttu Javasta emuloida. Tämä pätee kuitenkin vain sovelluksen selaimen puoleiseen osaan. Palvelimen puolella emulointia ei tehdä, vaan mahdollisia rajoituksia asettaa lähinnä asennuskonfiguraatio (J2EE:n versio, sovelluspalvelimen versio, jne.). Lista selainpuolella emuloiduista Java-kirjastoista löytyy osoitteesta:

http://code.google.com/webtoolkit/doc/1.6/RefJreEmulation.html

GWT:ssä on mukana omatkin käyttöliittymäkomponenttinsa, mutta tässä dokumentissa esiteltyjen harjoitustöiden osalta komponentteina käytetään Ext GWT:n komponentteja. Esimerkit on toteutettu GWT:n versiolla 1.7. Kirjoitushetkellä uusin versio oli  2.0.

 

Ext GWT:stÄ


Ext GWT on Java-kirjasto, joka tarvitsee osakseen ja laajentaa Google Web Toolkittiä. Ext GWT avulla on mahdollista toteuttaa Rikkaita Internet-sovelluksia, jollaiseksi Rich Internet-Applications (RIA) tavataan suomeksi kääntää. RIA-sovellukset muistuttavat toiminnoiltaan perinteisiä työpöytäsovelluksia, joita ajetaan webselaimessa ja joka keskustelee palvelimen kanssa asynkronisesti Ajax-teknologioita hyödyntäen.

Ext GWT tarjoaa runsaan joukon erilaisia käyttöliittymäkomponentteja, joita on nappuloista, erilaisista paneeleista ja ruudukoista asettelumanagereihin, kaavioihin ja työkalupalkkeihin. Komponenteilla on tietty perusulkoasu, mutta niitä voi kustomoida CSS-tyylisivustandardin avulla.  Kuten GWT:kin se tukee etäkutsuja (vastaussanomiksi kelpaavat niin JSON, XML kuin GWT RPC) ja Javascriptistä ei tarvitsee tietää välttämättä mitään (ohjelmointi Java-kielellä, Javan versio 1.5 ominaisuudet käytettävissä).

 

Google Web Toolkitin RPC (harjoitus 1)


Seuraavassa käydään läpi sellaisen yksinkertaisen GWT-sovelluksen vaatima koodi, missä palvelin vastaa metodikutsuun palauttamalla List-tietorakenteessa nipun itse luodun Message-luokan instansseja. Oman luokan instassin palauttamiseksi verkon yli on luokan implementoitava Serializable-rajapinta. Javan omien luokkien ja primitiivityyppien palauttamiseksi pätevät samat säännöt kuin tietotyyppien määrityksessä yleensäkin eli niiden geneerisyys on määriteltävä.

Kaikki esimerkkiohjelmaan liittyvät tiedostot, joita ohjelmoija itse on joutunut käsittelemään, näkyvät ylhäällä.

Hakemistoon war/hello GWT:n kääntäjä vie mm. optimisoidun Javascript-koodin ja muita tarpeellisiksi katsomiaan tiedostoja. Konfiguraatiotiedostossa hello/hello.gwt.xml on olennaista määritys entry-point. Se kertoo mistä luokasta ohjelman käynnistys alkaa. Luokka myös implementoi EntryPoint-rajapinnan.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<module rename-to="hello">

      <!-- Inherit the core Web Toolkit stuff.                        -->

      <inherits name="com.google.gwt.user.User" />

 

      <!-- Inherit the default GWT style sheet.  You can change       -->

      <!-- the theme of your GWT application by uncommenting          -->

      <!-- any one of the following lines.                            -->

      <inherits name="com.google.gwt.user.theme.standard.Standard" />

      <!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->

      <!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/>     -->

 

      <!-- Other module inherits                                      -->

 

      <!-- Specify the app entry point class.                         -->

      <entry-point class="hello.client.Hello" />

</module>

Kaavamaisesti GWT RPC -kutsun voisi esittää allaolevanlaisesti. Kuva on lähes vastaava kuin ohjelmistokehyksen verkkosivuilla oleva.

Sovelluksen käynnistävä luokka ei tee sen kummempaa kuin sen, että se heti alkuunsa lähettää asynkronisen kyselyn palvelimelle ja jää odottamaan vastausta. Vastauksen tullessa ajetaan metodi onSuccess, joka käsittelee onnistuneen kyselyn palauttaman vastauksen.


package hello.client;

 

import java.util.Iterator;

import java.util.List;

 

import com.google.gwt.core.client.EntryPoint;

import com.google.gwt.core.client.GWT;

import com.google.gwt.user.client.rpc.AsyncCallback;

import com.google.gwt.user.client.ui.Label;

import com.google.gwt.user.client.ui.RootPanel;

 

public class Hello implements EntryPoint {

 

      private final MessageFillerAsync messagefiller = GWT.create(MessageFiller.class);

 

      public void onModuleLoad() {

 

       AsyncCallback<List<Message>> callback = new AsyncCallback<List<Message>>() {

 

                @Override

                public void onFailure(Throwable caught) {}

 

                @Override

                public void onSuccess(List<Message> messages) {

                 for (Iterator iterator = messages.iterator(); iterator.hasNext();) {

                     Message message = (Message) iterator.next();

                     Label  label = new Label(message.getTitle());

                     RootPanel.get().add(label);

                 }

                }

       };

       messagefiller.getMessages("hello", callback);               

      }

}

MessageFiller-rajapinta implementoi RemoteService-rajapinnan, joka on marker interface ja listaa käytettävissä olevat metodit. Käynnistävässä luokassa luodaan (GWT.create) luokkailmentymä MessageFilleristä, josta tehdään tyyppimuunnos asynkroniseen versioon MessageFillerAsync. Tämän on tarkoitus määrittää takaisinkutsu-metodi, jota kutsutaan kun kysely on käsitelty palvelimella.

package hello.client;

 

import java.util.List;

import hello.client.Message;

import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

import com.google.gwt.user.client.rpc.RemoteService;

 

/**

 * The client side stub for the RPC service.

 */

@RemoteServiceRelativePath("messagefiller")

public interface MessageFiller extends RemoteService {

 

      public List<Message> getMessages(String keyword);

     

}


package hello.client;

 

import java.util.List;

import com.google.gwt.user.client.rpc.AsyncCallback;

 

/**

 * The async counterpart of <code>MessageFiller</code>.

 */

public interface MessageFillerAsync {

 

      public void getMessages(String keyword,

                AsyncCallback<List<Message>> callback);

 

}

Eclipseen asennettu GWT-plugini avustaa kehitystyön yhteydessä huomioimaan validin RPC-kutsun mallin luomisessa ja huomauttaa kyllä, jos geneerisyydet, tietotyypit tai parametrit ovat väärin.

Jäljellä olevan palvelinpuolen koodin lisäksi on vielä tiedoston war/WEB-INF/web.xml tiedoston muokkaaminen siltä osin, että sinne lisätään kutsuttavan servletin tiedot. Kun näitä tarvittavia toimenpiteitä tekee ensimmäisiä kertoja, niiden muistaminen voi tuntua hankalalta, mutta jo piankin ne muodostuvat triviaaliksi toimenpiteeksi. Sovelluksen koon kasvaessa voi olla hyödyllistä niputtaa ja lajitella metodikutsuja sopivasti nimettyihin ryhmiin (luokkiin ja paketteihin), jotta sovelluksen rakenteen hahmottaminen omassa mielessään olisi helpompaa.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE web-app

    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"

    "http://java.sun.com/dtd/web-app_2_3.dtd">

 

<web-app>

 

  <!-- Servlets -->

  <servlet>

    <servlet-name>MessageFillerServlet</servlet-name>

    <servlet-class>hello.server.MessageFillerServlet</servlet-class>

  </servlet>

 

  <servlet-mapping>

    <servlet-name>MessageFillerServlet</servlet-name>

    <url-pattern>/hello/messagefiller</url-pattern>

  </servlet-mapping>

 

  <!-- Default page to serve -->

  <welcome-file-list>

    <welcome-file>hello.html</welcome-file>

  </welcome-file-list>

 

</web-app>

MessageFillerServlet-luokka sisältää MessageFiller-rajapinnassa määritellyn getMessages-metodin. Tässä tapauksessa se kääräisee List-tietorakenteeseen 3 kpl Message-luokan ilmentymiä, joihin on kirjattu mukaan lyhyt viesti.

package hello.server;

 

import java.util.ArrayList;

import java.util.List;

 

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import hello.client.MessageFiller;

import hello.client.Message;

 

/**

 * The server side implementation of the RPC service.

 */

@SuppressWarnings("serial")

public class MessageFillerServlet extends RemoteServiceServlet implements

       MessageFiller {

 

      public List<Message> getMessages(String keyword) {

 

       List<Message> messages = new ArrayList<Message>();

 

       if (keyword.equals("hello")) {

 

                for (int i = 0; i < 3; i++) {

                 Message message = new Message();

                 message.setTitle("Hello message nro " + i);

                 messages.add(message);

                }

               

       }

       return messages;

      }

}

 Message-luokka:

package hello.client;

import java.io.Serializable;

 

public class Message implements Serializable {

 

      private String title;

      private String content;

 

      public String getTitle() {

       return title;

      }

 

      public void setTitle(String title) {

       this.title = title;

      }

 

      public String getContent() {

       return content;

      }

 

      public void setContent(String content) {

       this.content = content;

      }

}

On tiettyjä rajoitteita ja ehtoja sille, mitä voidaan välittää serialisoituna. Tietotyyppi on serialisoitavissa, jos jokin seuraavista ehdoista pätee:

- primitiivityyppi, kuten char, byte, short, int, long, boolean, float tai double.
- Stringin tai Daten instanssi, tai primitiivityypin kääreluokka kuten Character, Byte, Short, Integer, Long, Boolean, Float tai Double.
- serialisoivan rajapinnan implementoiva luokka

Muista ehdoista lisää tietoa osoitteesta:
http://code.google.com/webtoolkit/doc/1.6/DevGuideServerCommunication.html#DevGuideSerializableTypes

 

Vastaussanomien tietorakenteita tietokantakyselyissÄ (harjoitus 2)


GWT RPC -etäkutsujen toiminnan ja käytettävyyden ymmärtämiseksi on harjoituksen vuoksi kehitelty graafisen sovelluksen prototyyppi, joka toteuttaa kolme melko tavanomaista tietokantakyselyä (select- tai insert-lauseita), joiden palauttamaa tietoa transformoidaan sopiviksi tietorakenteiksi ja palautetaan tuo tietorakenne sovelluksen asiakasosalle.

 

5.1 KÄyttÖtapauskaavio

Harjoitussovelluksen kuvitellulla käyttäjällä on käytettävissään mm. seuraavanlaisia toimintoja (käyttötapaukset).

 

Vain yhden etäkutsun aiheuttavat toiminnot ovat sekvenssikaavioilla (esimerkeissä käytetyt sekvenssikaaviot RUP:n analyysivaiheesta) ajateltuina varsin samanlaisia:

  1. sovelluksen asiakasosa lähettää asynkronisen etäkutsun, mahdollisesti ensin kutsun muoton hiukan hioen
  2. sovelluksen palvelinosa ottaa kutsun käsiteltäväkseen
  3. haetaan tietoa tietokannasta ja transformoidaan tieto jotain tietorakennetta vastaavaan muotoon
  4. vastausviesti lähtee matkaan
  5. sovelluksen asiakasosa käsittelee vastausviestin

5.2 Vastaussanoman tietorakenteena serialisoitu luokka

Käyttötapaus: Sisäänkirjautuminen

 

Tässä tarvitaan kaksi etäkutsua,  sisäänkirjautumisen tekemiseksi tietoturvallisemmaksi. Selainpuolella tässä käytetään sellaista netistä löytämääni SHA1-algoritmia, joka käyttää vain GWT:n emuloinnin hyväksymiä Java-metodeita. Selainpuolella käytetään Java-luokkaa MessageDigest SHA1-muotoiseen kryptaamiseen. Jälkimmäinen kutsu palauttaa oman Profile-luokan instanssin, joka toteuttaa IsSerializable-rajapinnan, jonka avulla GWT ymmärtää suorittaa automaattisen serialisoinnin siirrettävälle luokan instanssille. Vastaussanomana saadun Profile-olion sisältöä jatkokäsitellään selainpuolella.

Tässä toteutuu suunnittelumalli double hashing. Katkelma kirjasta Ajax Design Patterns (korostukset omat):

With double hashing, the server generates a one-time random seed. The browser then hashes twice: first, it hashes the password to yield what's hopefully stored on the database. But instead of sending that, the browser combines it with the one-time seed to form a new hash. This new hash is sent to the server. The server then pulls out the stored hash from the database and combines it with the original one-time seed to form a new hash, which must match the hash that was uploaded. This works because in both cases, the initial password has been passed through the same two hash functions. In the browser, the user's attempt is passed through a fixed hash function and the result is immediately passed to a new hash function with one-time seed. And in the server, the database already holds the result of hashing the real password using the fixed hash function.

Kyseinen Michael Mahemoffin Ajax-kirja näytti olevan saatavilla osoitteessa:
http://www.scribd.com/doc/15490788/Ajax-Design-Patterns-by-OReilly-Media

SHA1-algoritmin Java-implementaatio saatavilla osoitteessa:
http://www.docjar.com/html/api/Freenet/crypt/SHA1.java.html

Otteita kutsusta 1,  joka palauttaa natiivin HashMap-luokan

Palautettavan HashMapin alustus
HashMap<String, String> seedMap = new HashMap<String, String>();

Tietokantakyselyn valmistelu
PreparedStatement query = connection.prepareStatement("INSERT INTO seed (seed) VALUES (?)", Statement.RETURN_GENERATED_KEYS);

Tietokantakyselyn aukkokohtien täyttäminen
query.setString(1, seedMap.get("seed"));

Tietokantakyselyn suorittaminen
int resultIndicator = query.executeUpdate();

Tietokantakyselyn vastauksen talteen ottaminen
ResultSet rs = query.getGeneratedKeys();

Vastaussanoman muodostus
generatedId = rs.getString(1);
seedMap.put("seedid", generatedId);


Otteita kutsusta 2, joka palauttaa oman Profile-luokan, joka toteuttaa IsSerializable-rajanpinnan

Kryptauksen alustus
md = MessageDigest.getInstance("SHA-1");

Vertailtavan hashin muodostus
String concatted = passwordHash.concat(dbSeed);
String uppercased = concatted.toUpperCase();
byte[] digest = md.digest(uppercased.getBytes());
String temp = digest.toString();
BigInteger bigInt = new BigInteger(1, digest);
String hash = bigInt.toString(16);

Tietokantakyselyn valmistelu
PreparedStatement query = connection.prepareStatement("SELECT s.seed, ah.passwordhash, ah.forename, ah.surname " + "FROM seed s, accountholder ah " + "WHERE s.seedid = ? AND ah.loginname = ?");

Vastaussanoman muodostus
Profile profile = new Profile();
profile.setLoginname(accountHash.get("loginname"));
profile.setForename(firstName);
profile.setSurname(lastName);

 

 


5.3 Vastaussanoman tietorakenteena HTML-koodia String-kÄÄreessÄ

Käyttötapaus: Klikkaa sanan selitettäväksi

Tässä hyödynnetään laajaa WordNet-sanalistaa, josta oli saatavilla sellaisenaan mm. MySQL-tietokannan tauluiksi siirrettävissä oleva nippu SQL-skriptejä. WordNet on nykyisin Princetonin yliopiston vaalima projekti, joka sisältää muutakin ja on tarkoitettu paljon muuhunkin kuin vain yksittäisten sanojen kuvailuun. Kuvaus yliopiston sivuilta:

WordNet® is a large lexical database of English, developed under the direction of George A. Miller. Nouns, verbs, adjectives and adverbs are grouped into sets of cognitive synonyms (synsets), each expressing a distinct concept. Synsets are interlinked by means of conceptual-semantic and lexical relations. The resulting network of meaningfully related words and concepts can be navigated with the browser. WordNet is also freely and publicly available for download. WordNet's structure makes it a useful tool for computational linguistics and natural language processing.

Vastaussanomana palautetaan String-tietotyyppinä HTML-standardin mukaisessa muodossa olevaa tekstisisältöä, joka sijoitetaan selaimen puolella paneelin sisällöksi sellaisenaan. Tietokannan palauttamista riveistä muodostetaan ensin DOM-puu, joka sitten transformoidaan XSLT-tyylitiedostolla HTML:lläksi.

WordNet-sivusto:
http://wordnet.princeton.edu

Wordnet SQL  Builder:
http://wnsqlbuilder.sourceforge.net


Otteita kutsusta, joka palauttaa String-tietotyypissä HTML-koodia

Transformoijan alustus (oma luokka)
Morph morph = new Morph();
morph.BuildDoc("definitions");

Tietokantakysely ilman valmistelua
String queryString = "select w.lemma, sy.definition " + "FROM words w " + "LEFT JOIN senses se ON w.wordid = se.wordid " + "LEFT JOIN synsets sy ON se.synsetid = sy.synsetid " + "where w.lemma = '" + word + "'";

Tietokantakyselyn suorittaminen ja vastauksen talteenotto samassa
ResultSet rs = connection.createStatement().executeQuery(queryString);

Muodostetaan tietokannan palauttamista riveistä  DOM-puu
morph.FillDocAttr(rs, "row");

Katkelma Morph-luokan FillDocAttr-metodista
ResultSetMetaData rsmd = rs.getMetaData();
int colCount = rsmd.getColumnCount();
while (rs.next()) {
Element row = doc.createElement(rowname);
results.appendChild(row);
for (int i = 1; i <= colCount; i++) {
String columnName = rsmd.getColumnName(i);
Object value = rs.getObject(i);
Attr attr = doc.createAttribute(columnName);
attr.setValue(value.toString());
row.setAttributeNode(attr);

Vastauksen transformointi XSLT-tyylisivulla
morph.Transform("sample-definition.xsl", "html");
output = morph.getTransformOutput();

Katkelma Morph-luokan XSLT-transformoijasta
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.setOutputProperty(OutputKeys.METHOD, method);
transformer.setOutputProperty(OutputKeys.ENCODING, "ISO-8859-1");
StringWriter sw = new StringWriter();
StreamResult sr = new StreamResult(sw);
transformer.transform(domSource, sr);
transformOutput = sw.toString();


 


5.4 Vastaussanoman tietorakenteena XML, Grid-komponentille

Käyttötapaus: Haku hakusanalla

Tässä tapauksessa Grid-komponenttiin on liitetty joukko sisällön lataamisen ja hallitsemiseen erikoistuneita luokkia. Haettaessa hakusanalla, tullaan suorittaneeksi etäkutsu, joka tiedustelee tietokannalta, joska hakusanaa vastaavia kuvia löytyisi ja jos löytyy, palautetaan kuvista lista String-tietotyypissä, joka sisältää XML-muotoista dataa.

 

Otteita kutsusta, joka palauttaa String-tietotyypissä XML-koodia

Välipalvelin RPC:lle (ei tarvita kaikissa asynkronisissa kutsuissa)
RpcProxy<String> proxy = new RpcProxy<String>() { @Override
protected void load(Object loadConfig, AsyncCallback<String> callback) {
searchfiller.getSomePhotos(searchstring.getValue(), callback);} };

Grid-komponentin lataimet
XmlLoadResultReader<ListLoadResult<ModelData>> reader = new XmlLoadResultReader<ListLoadResult<ModelData>>(type);
final BaseListLoader<ListLoadResult<ModelData>> loader = new BaseListLoader<ListLoadResult<ModelData>>(proxy, reader);

Grid-komponentin sisällön säilöjä
final ListStore<ModelData> store = new ListStore<ModelData>(loader)

Grid-komponentti itse
Grid<ModelData> grid = new Grid<ModelData>(store, cm);

Tietokantakyselyn valmistelu
PreparedStatement query = connection.prepareStatement("SELECT id, title, ownerloginname FROM photo WHERE FIND_IN_SET('"+ ? + "',photo.tags) > 0");

 

 


Otteita tietokannan tÄyttÖskriptistÄ


Tietokannan rakennetta ei sovelluksen (harjoitus 2) protyyppiä varten ole kummoisemmin kasvatettu käyttäen käsiteanalyysiä ja ER-kaavioita, vaan taulut on luotu ihan perinteistä mutua käyttäen. Seed- ja session-tauluissa esiintyvät samana toistuvat merkkisarjat johtuvat siitä, että arvot ovat ennalta määritetty. Todellisuudessa ne arvottaisiin satunnaisesti.

INSERT INTO `accountholder` (`loginname`, `passwordhash`, `forename`, `surname`) VALUES
('hessu','D52D2540417AF7940F01837B9A706A4341E92557','Harald','Henriksson'),
('niilo','42C3AF9C9F3B340A51FA778E5E3285B10F06BF08','Niilo','Paasilinna'),
('pacce','D9E4C803CC8977FA6EE6783785778EEE7FBA4B32','Pacce','Niklansson');
COMMIT;

INSERT INTO `eventype` (`eventtype`, `description`) VALUES
('ownerchange','Ownership of an image is changed.');
COMMIT;

INSERT INTO `photo` (`id`, `title`, `tags`, `ownerloginname`, `price`, `inquiredby`) VALUES
(1,'Aatami kylvyssä','adam,bath,water,bubble','hessu',8,'niilo'),
(2,'Maarianhaminassa','island,view,grass,rock,boat','pacce',16,''),
(3,'Huoltoasemalla','petrol,station,oil,tank,car','niilo',32,''),
(4,'Kuunvalo','moon,light,dim,night,dark','niilo',64,'hessu'),
(5,'Presidentinlinnan eteinen','president,building,government','hessu',32,'niilo'),
(6,'Ninjakuoro','ninja,chorus,singin,song,soundwave,happy','hessu',8,''),
(7,'Unikkopelto','field,drug,leaf,plant','hessu',128,'');
COMMIT;

INSERT INTO `photoediting` (`id`, `photoid`, `event`, `earliername`, `newtitle`, `earliertags`, `newtags`, `date`) VALUES
(1,1,'upload',NULL,NULL,NULL,NULL,'2009-09-14 23:22:18'),
(2,2,'upload',NULL,NULL,NULL,NULL,'2009-09-14 23:22:18'),
(3,3,'upload',NULL,NULL,NULL,NULL,'2009-09-14 23:22:18'),
(4,4,'upload',NULL,NULL,NULL,NULL,'2009-09-14 23:22:18'),
(5,5,'upload',NULL,NULL,NULL,NULL,'2009-09-14 23:22:18'),
(6,6,'upload',NULL,NULL,NULL,NULL,'2009-09-14 23:22:18'),
(7,7,'upload',NULL,NULL,NULL,NULL,'2009-09-14 23:22:18');
COMMIT;

INSERT INTO `photoevent` (`id`, `photoid`, `event`, `fromwhomloginname`, `towhomloginname`, `date`) VALUES
(8,1,'cancelownerchange','pacce','hessu','2009-10-17 01:52:20'),
(9,2,'cancelownerchange','pacce','hessu','2009-10-17 01:52:20'),
(10,1,'ownerchange','pacce','hessu','2009-10-17 01:55:00'),
(11,2,'ownerchange','pacce','hessu','2009-10-17 01:55:00'),
(12,1,'cancelownerchange','pacce','hessu','2009-10-17 01:55:16'),
(13,2,'cancelownerchange','pacce','hessu','2009-10-17 01:55:16'),
(14,3,'approvephotoownerchange','pacce','niilo','2009-10-17 02:45:14'),
(15,7,'declinephotoownerchange','pacce','niilo','2009-10-17 02:45:53'),
(16,1,'photoinquire','pacce','hessu','2009-10-17 04:07:08'),
COMMIT;

INSERT INTO `seed` (`seedid`, `seed`, `addingtime`) VALUES
(51,'4yrhrh54','2009-10-16 23:45:50'),
(52,'4yrhrh54','2009-10-17 00:42:33'),
(53,'4yrhrh54','2009-10-17 01:05:34'),
COMMIT;

INSERT INTO `session` (`id`, `sessionid`, `loginname`, `addingtime`) VALUES
(67,'12345678901234567890123456789012','hessu','2009-10-16 23:45:50'),
(68,'12345678901234567890123456789012','hessu','2009-10-17 00:42:33'),
(69,'12345678901234567890123456789012','hessu','2009-10-17 01:05:34'),
COMMIT;


JDBC


Yksittäiseen tietokantaan kytkeytymiseksi Java-sovellus tarvitsee JDBC-ajurin (Java Database Connectivity API), joka osaa käsitellä käytettävää tietokantajärjestelmää. API koostuu kahdesta rajapinnasta, joista toinen on Java-sovelluksen ohjelmoijalle ja toinen ajurien kirjoittajille. Sovellusrajapinta on käytetystä tietokannasta riippumaton, mutta tietokantoja itseään varten tarvitaan erillinen ajuri, jonka tietokannan valmistaja on tehnyt ja saataville julkaissut. Näitä JDBC-ajureita on tarjolla mm. relaatiotietokantoihin Oracle Database, Sybase, IBM DB2, Microsoft SQL Server, MySQL ja PostGreSQL). Sovellusrajapinnan tietokantariippumattomuus tarkoittaa sitä, että  samaa ohjelmointikoodia voi käyttää esim. niin Oracle- kuin MySQL-tietokannankin kohdalla.

 

7.1 Vaiheet tietokannan kÄyttÄmiseksi Java-ohjelmassa


Ajurin lataaminen:

String driver = "com.mysql.jdbc.Driver";
Class.forName(driver).newInstance();

Yhteysosoitteen ja tunnusten määritys:

String url = "jdbc:mysql://localhost:3306/";
String db = "databasename";
String user = "username";
String pass = "password";

Yhteyden muodostus:

Connection conn = DriverManager.getConnection(url + db, user, pass);

Luodaan Statement olio SQL-lauseiden suorittamiseksi:

PreparedStatement query = connection.prepareStatement("SELECT s.seed, ah.passwordhash, ah.forename, ah.surname " + "FROM seed s, accountholder ah " + "WHERE s.seedid = ? AND ah.loginname = ?");
query.setInt(1, Integer.parseInt(accountHash.get("seedid")));
query.setString(2, accountHash.get("loginname"));

Suoritetaan SQL-lause:

ResultSet result;
result = query.executeQuery();

Käydään tulosjoukko läpi:

while (result.next()) {
dbSeed = result.getString("seed");
passwordHash = result.getString("passwordhash");
firstName = result.getString("forename");
lastName = result.getString("surname");
}

Suljetaan ResultSet, Statament ja Connection:

query.close();
rs.close();
connection.close();

Yhteysosoite on aina muotoa protokolla:aliprotokolla:tietolähde. Protokollana on aina JDBC, mutta aliprotokolla on ajurikohtainen (esimerkiksi mysql tai odbc).

Yhden JDBC-yhteyden aikana voidaan suorittaa useita SQL-käskyjä tai -päivityksiä, jotka ajuri välittää tietolähteelle. Jokainen SQL-lause itsessään on transaktio eli tapahtumakokonaisuus, jonka voi suorituksen jälkeen peruuttaa, mutta transaktio voi koostua useammastakin SQL-lauseesta.

Yhteyden avaaminen aloittaa automaattisesti uuden transaktion, joka saatetaan päättymään itsestään jokaisen yksittäisen SQL-lauseen ajamisen jälkeen. Käytännössä Connection-olion commit-metodi ajetaan tuolloin automaattisesti, mikä tarkoittaa myös sitä, että kyseistä transaktiota ei voi enää peruuttaa vain tieto on kirjoitettu pysyvästi tietokantaan. Metodilla setAutoCommit automaattisen transaktion päättymisen voi kytkeä pois päältä. Tällöin transaktion rajoissa voi suorittaa yhden tai useampia SQL-lauseita, jotka voi peruuttaa rollback-metodilla. Suorituksen lopuksi ajetaan commit-metodi ja kytketään setAutoCommit takaisin päälle.

PreparedStatement on Statement-olion alaluokka. Olennaisena erottavana piirteenä on se, että esivalmisteltu kysely lähetetään tietokannalle heti kun se on luotu, jossa se esivalmistellaan jo ennen kuin varsinaista SQL-lauseen suorittavaa käskyä on ajettu. Jos samaa SQL-lausetta tarvitaan myöhemmin uudestaan, sitä on nopeampi käyttää, mutta tämä on vain yhteyskohtaista.

Yhteyden voi luoda myös DataSource-rajapintaa käyttäen. Esimerkissä yhteys luotiin DriverManager-rajapinnan avulla. DataSource tarjoaa paremman reitin tietolähteeseen kytkeytymiseen ja tunnistautumiselle, tehden ohjelmointikoodista vielä hieman helpommin alustasta tai järjestelmästä toiseen siirrettävän. Se mahdollistaa myös tietokantayhteysaltaan käytön ja hajautetut transaktiot. Tietokantayhteyden avaaminen on aina hidas operaatio, ja turhia yhteyden avaamisia tulisi välttää. Sovelluksen tarvitessa tietokantayhteyttä se ”lainataan” altaasta.

 

7.2 JDBC-ajureita

JDBC:n tietokantakohtaiset ajurit luokitellaan neljään eri luokkaan, joista kolmas ja neljäs ovat  puhdasta Javaa. Tyypin neljä ajuri kääntää JDBC-kutsut suoraan tietokantajärjestelmän käyttämän verkkoprotokollan ymmärtämään muotoon. Tyypin kolme ajurit kääntävät kutsut väliohjelmiston valmistajan protokollalle, joka sitten vuorostaan kääntää kutsut sille tietokannalle, joka on kytkettynä väliohjelmiston palvelimeen. Jälkimmäinen mahdollistaa sen, että samalla yhteydellä voi olla yhteydessä useampaan erityyppiseen tietokantaan yhtä aikaa. Alla pieni otos saataville olevista JDBC-ajureista:

PostgreSQL JDBC Driver
http://jdbc.postgresql.org/

Sybase – jConnect for JDBC
http://www.sybase.com/products/allproductsa-z/softwaredeveloperkit/jconnect

Oracle – JDBC driver ja Universal Connection Pool (UCP)
http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/index.html

MySQL – Connector-J
http://dev.mysql.com/downloads/connector/j/

SQL Server – JDBC Driver 2.0 Documentation
http://msdn.microsoft.com/en-us/library/ee229547%28SQL.10%29.aspx


Termiluettelo


Ajax = asynkroninen tiedonsiirtotekniikka, jolla websovelluksista voi tehdä vuorovaikutteisempia

ajuri = tarjoaa ohjelmistokehittäjälle määritellyn rajapinnan, jonka kautta jonkin laitteen toimintaa voi ohjata

analyysivaihe = eräs RUP-ohjelmistosuunnittelumallin vaiheista

asiakas = vastinpari palvelimelle, voi olla esim. työpöytäsovellus, websovellus tai yksittäinen websivu

asynkroninen viestintä = odotellessa vastausta viestiin muu sovelluksen toiminta jatkuu

DOM-puu = viittaa websivun/-sovelluksen sisäiseen rakenteeseen

Eclipse = eräs ohjelmistokehitysympäristö

emulointi = jonkin toiminnan jäljittely

Entry-point = viittaus Google Web Toolkittiä käyttävän sovelluksen aloitusluokkaan, joka suoritetaan käynnistettäessä

etäkutsu = sovellus kutsuu sellaista metodia, joka ei ole saatavilla paikallisesti, kutsutaan verkon yli/kautta

Ext GWT = Java-kirjasto, joka tarvitsee osakseen ja laajentaa Google Web Toolkittiä.

geneerisyys = tietorakenteiden ja säilöjen tyyppimääritys

GWT = Google Web Toolkit, ohjelmistokehys Ajax-sovellusten kehittämiseen

HTML = HyperText Markup Language, sivunkuvauskieli, josta erityisesti websivut rakentuvat

instanssi = luokan ilmentymä

J2EE = ohjelmistokehitysalusta ja spesifikaatio Java-sovellusten/-sovelluspalvelimien kehittämiseen

Java = eräs ohjelmointikieli

Javascript = eräs webselaimissa käytettävä ohjelmointikieli

JDBC = tietokantariippumattomien sovellusten tekemisen mahdollistava ohjelmointirajapinta

kirjasto = koostuu hierarkisesti lajitelluista luokista, järjesteltyinä paketteihin

konfiguraatiotiedosto = sisältää sovelluksen käynnistymiseen ja toimintaan vaikuttavia säätöjä

kryptaus = tiedon muuntaminen salattuun muotoon

käyttötapaus = yksittäinen UML-ohjelmistonmallinnuskielen mukainen selite käyttäjän toimintamahdollisuudella

käyttötapauskaavio = sisältää kaikki käyttäjälle mahdolliset toiminnot

kääntäjä = tuottaa lähdekoodin pohjalta ajettavan kokonaisuuden

kääreluokka = olion luokka, jota käytetään luokattoman primitiivityypin sisällyttämiseen

luokka = metodeista ja attribuuteista koostuva olion määritelmä

market-interface = käytetään liittämään metadataa luokkiin sellaisessa kielessä, joka ei tälläistä annotointia tue

metodi = luokan/olion sisältämä toiminto

olio = luokan ilmentymä

optimointi = jonkin toiminnan tehostaminen

paketti = nimetty osa hierarkiaa, joka sisältää joukon alipaketteja ja/tai luokkia

palvelin = vastinpari asiakkaalle, tarkoittaen tässä yhteydessä sovelluksen fyysisen palvelimen puoleista osaa

primitiivityyppi = tietotyypin alkeistyyppi

protokolla = yhteyskäytäntö ja standardi, joka määrittelee osapuolten väliset yhteydet

prototyyppi = sovelluksen nopeahkosti luotu versio, jota voi käyttää tulevien aikomusten hahmottamisen apuna

rajapinta = joukko metodeita, jotka niitä käyttävä luokka on toteuttanut rajapintaluokan nimeämästi

RIA = Rich Internet Applications, websovellus, jolla on samantapaisia piirteitä kuin työpöytäsovelluksilla

RPC = Remote Procedure Call, yleisnimike etäkutsujen toteuttamiselle

RUP = Rational Unified Process, iteratiivinen ohjelmistokehitysmalli

sekvenssikaavio = UML-kaavio, joka kuvaa olioiden välistä toimintaa

serialisointi = olion siirtäminen verkon yli muuntamalla olio yhteysprotokollan kelpuuttamaan muotoon

SHA1-algoritmi = kryptausmetodi, jolla tavataan suojata esim. salasanoja

SQL = Structured Query Language, standardoitu kyselykieli, jolla relaatiotietokantaa käskytetään

tietorakenne = Java-kielessä tietotyyppi, joka sisältää tietyn rakenteista tietoa

tietotyyppi = primitiivityyppi tai määritellyn tyyppistä tietoa kuvaava luokka

transaktio = joukko tapahtumia, jotka suoritetaan perätysten

transformointi = tietojoukon muuntaminen jonkin mallin mukaan toisenlaiseksi

websovellus = itsenäinen sovellus tai palvelu, joka käynnistyy tiettyyn webosoitteen mentäessä

XML =  eXtensible Markup Language, merkintäkieli ja standardi, jolla tietomassoja voi jäsennellä ja kuvailla

XMLHttpRequest = DOMin rajapinta, jonka kautta websovellus keskustelee palvelimen kanssa

XSLT = Extensible  Stylesheet Language Transformations, kieli XML-dokumenttien transformoimiseen