Opas tietokantasovellusten tekemisen aloittamiseen Java-kielellä, osa 4

Edellisessä osassa lähetettiin lomakkeesta tietoa Servletille, ja tallennettiin tieto tietovarastoon. Edellisessä osiossa käytetty tietorakenne on käytännössä staattinen lista Uimari-olioita, joka tuhoutuu aina kun ohjelmisto käynnistyy uudestaan.

Tässä osassa tutustutaan NetBeanssissa olevan tietokannan käyttöön (Java DB), sekä muutetaan edellisissä osissa tutuksi tullut uimari tietokantaan tallennettavaksi.

Ongelmia?

Tässä osiossa käydään ensiksi läpi ongelmia joihin saatat törmätä. Tietokantasovelluksissa käytetään usein ulkopuolisia kirjastoja, joita ei aina löydy suoraan kehitysympäristöistä.

Tietokannan luominen

Aktivoi Services-välilehti NetBeansista (Window -> Services). Services-lista listaa erilaisia palveluita, joita NetBeansiin on integroitu. Kaikkia alla olevassa kuvassa näkyviä palveluita ei tarvita, tämän osion tärkein on Databases-valikko.

Klikkaa Databases-lista auki. Klikkaa oikealla hiirennäppäimellä Java DB-vaihtoehtoa ja valitse "Create Database". Valitse tietokannan nimeksi uimarit ja käyttäjätunnukseksi ja salasanaksi (esimerkiksi) u.

Kun klikkaat ok, NetBeans luo tietokannan "uimarit" omaan kehitysympäristöösi. Näet myös, että Java DB:n alle ilmestyy kanta uimarit, sekä JDBC-yhteys tietokantaan. JDBC, tuttavallisemmin Java Database Connectivity API, on ohjelmointirajapinta jota käytetään yhteyden luomiseen tietokantaan. Lisätietoa JDBCstä löytyy mm. Wikipediasta!

Tietokantasovellusten käyttäminen suoraan JDBCn yli on mahdollista DriverManager-luokan avulla, jolla voidaan ottaa yhteys tietokantaan ja tehdä SQL-kyselyitä. Tätä lähestymistapaa ei kuitenkaan juurikaan käytetä. Relaatiotietokantoja käytetään usein, mutta ei aina, ns Object-relational mapping-tekniikan (ORM) avulla, jossa luokat pyritään "mäppäämään" suoraan tietokantatauluihin.

JPA

JPA, tuttavallisemmin Java Persistence API, on Javan oma ORM spesifikaatio. Se tarjoaa mahdollisuuden luoda luokkia, jotka voi tallentaa suoraan tietokantaan. Tietokantaan tallennettavat luokat annotoidaan merkinnällä Entity, joka kertoo niiden olevan tallennettavissa. Luokkien ilmentymiä, eli olioita, tallennetaan JPA-spesifikaatiossa luokalla EntityManager. Tästä Oraclen JPA-oppaaseen.

Luodaan ensiksi tietokantakonfiguraatio, persistence.xml. Valitse "New File", etsi kategoria "Persistence", ja valitse "Persistence Unit".

Valitse seuraavalla sivulla kirjastoksi (Persistence Library) EclipseLink (JPA 2.0), tietokantayhteydeksi juuri luotu jdbc-yhteys uimarit-tietokantaan, sekä taulujen luontistrategiaksi Drop and Create (taulut luodaan uudestaan joka käynnistyksellä -- hyvä vaihtoehto sovellusta kehittäessä -- hyvin huono tuotannossa.).

Paina Finish, ja edessäsi on konfiguraatiotiedosto.

Konfiguraatiotiedosto persistence.xml sisältää tiedon osoitteesta, jonne yhteys otetaan sekä luokista, joita tallennetaan kantaan (Entity Classes). Yksittäistä konfiguraatiota, joka sisältää yhteysosoitteen sekä luokat kutsutaan Persistence Unitiksi, ja niitä voi olla useampia. Esimerkiksi yksi voi osoittaa omalla koneella pyörivään hiekkalaatikkoon, ja toinen tuotantoon.

Uimari

Muutetaan uimaria seuraavaksi siten, että sen voi tallentaa tietokantaan. Tietokantaan tallennettavat oliot merkitään @Entity-annotaatiolla. Entity-annotaation lisäksi jokaisella tallennettavalla luokalla on oltava @Id-tunnuksellä annotoitu kenttä, joka toimii vastaavan taulun ensisijaisena avaimena. ORM-kehyksissä id on usein long tai int, käytämme esimerkeissä tyyppiä long. Annotaatio @GeneratedValue(strategy = GenerationType.AUTO) kertoo että id-kenttä täytetään automaattisesti. Muokataan luokkaa vielä siten, että se toteuttaa rajapinnan Serializable, ja lisätään sille tyhjä konstruktori.

Tällä hetkellä attribuutit nimi ja syntymaVuosi eivät tallentuisi tietokantaan. Annotaatio @Column ilmaisee attribuuttien kuuluvan taulun sarakkeiksi. Luodaan samalla myös getterit ja setterit jokaiselle attribuutille.

package harjoitus;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Uimari implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column
    private String nimi;

    @Column
    private int syntymaVuosi;

    public Uimari() {
    }

    public Uimari(String nimi, int syntymaVuosi) {
        this.nimi = nimi;
        this.syntymaVuosi = syntymaVuosi;
    }

    public long getId() {
        return id;
    }

    public String getNimi() {
        return nimi;
    }

    public int getSyntymaVuosi() {
        return syntymaVuosi;
    }
}

Rekisteri

Muokataan rekisteriä siten, että se käyttää aiemmin luotua tietokantakonfiguraatiota. Tietokantakonfiguraatio UimariHarjoitusPU löytyy persistence.xml-tiedostosta. Poistetaan aiemmin käytössä ollut lista, ja korvataan se siten, että metodit tekevät operaatiot tietokantaan.

Metodit käsittelevät tietokantaa EntityManager-olion avulla, joka hallinnoi tietokantaan tallennettavia luokkien ilmentymiä. Uimarin lisääminen tapahtuu metodilla persist, joka vaatii toimiakseen transaktion. Metodi lisaaUimari näyttää miten uimari lisätään tietokantaan.

Kaikkien uimarien listaamiseen on useampikin lähestymistapa. Alla olevassa esimerkissä luodaan kysely käsin. Vaikka kyselyn syntaksi näyttää vahvasti SQL:ltä, on JPA:n syntaksi hieman erilainen -- Opas JPQL:n saloihin löytyy Oraclen JavaEE5 oppaan osasta 27.

public class Rekisteri {

    private EntityManagerFactory emf = null;

    public Rekisteri() {
        // käytetään "UimariHarjoitusPU"-konfiguraatiota
        emf = Persistence.createEntityManagerFactory("UimariHarjoitusPU");
    }

    public EntityManager getEntityManager() {
        return emf.createEntityManager();
    }

    // lisaa uimari
    public void lisaaUimari(Uimari uimari) {
        EntityManager em = getEntityManager();

	// aloitetaan transaktio
        em.getTransaction().begin();
	// tallennetaan uimari
        em.persist(uimari);
	// lopetetaan transaktio sanomalla commit
        em.getTransaction().commit();
    }

    // hae kaikki uimarit
    public List<Uimari> getUimarit() {
        EntityManager em = getEntityManager();
        return em.createQuery("SELECT u FROM Uimari u").getResultList();
    }
}

Tietokantakyselyn voi tehdä myös ohjelmallisesti. Esimerkiksi vastaavan getUimarit-kutsun voisi rakentaa seuraavasi. Yksi opas löytyy seuraavasta linkistä.

    public List<Uimari> getUimarit() {
        EntityManager em = getEntityManager();
        CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
        cq.select(cq.from(Uimari.class));
        Query q = em.createQuery(cq);

        return q.getResultList();
    }

Kun käynnistät palvelimen ja käytät rekisteriä, tiedot menevät tietokantaan. Huhhuh!

Uimari kuuluu aina uimaseuraan...