Osa 5

Tietokannan käsittely sovelluskehyksen avulla

Tietokantoja käsitellään tyypillisesti sovelluskehysten avulla. Tutustutaan seuraavaksi lyhyesti tietokantakyselyiden tekemiseen Spring-sovelluskehyksen avulla. Käytämme esimerkeissä Spring-sovelluskehyksen luokkaa JdbcTemplate, joka abstrahoi tietokantayhteyden muodostamisen sekä tietokantayhteyteen liittyvien resurssien vapauttamisen.

Mikäli haluat luoda oman tietokantaa käyttävän Spring-projektin tyhjästä, osoitteessa https://spring.io/guides/gs/relational-data-access/ oleva opas on hyvä lähtökohta.

Tietokantayhteyden määrittely

Tietokantayhteys määritellään Spring-projekteissa application.properties-tiedoston avulla. Tiedostoon määritellään osoite, tietokantayhteyden luomiseen käytettävä ajuri, käyttäjätunnus, salasana, sekä mahdollisesti muita lisäominaisuuksia. Tiedosto lisätään projektin kansioon src/main/resources.

Alla oleva sisältö tarjoaisi yhteyden projektin juuressa olevaan tietokantatiedostoon tietokanta.

Esimerkkitulostus

spring.datasource.url=jdbc:h2:./tietokanta spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password=

spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

Ensimmäinen komentorivisovellus

Komentorivisovellus luodaan toteuttamalla Spring-sovelluskehyksen CommandLineRunner-rajapinta. Kaikki (tämän kurssin puitteissa käsiteltävät) Spring-sovellukset käynnistetään luokasta, jossa on annotaatio @SpringBootApplication.

Yksinkertainen komentorivisovellus näyttää seuraavalta.

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class NimetSpringSovellus implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(NimetSpringSovellus.class);
    }

    @Override
    public void run(String... args) throws Exception {
        // Komentorivisovelluksen toiminnallisuus
        System.out.println("Hei maailma!");
    }
}

Sovelluksen suorittaminen tulostaa viestin Hei maailma!.

Sovelluskehyksen tarjoamien apuvälineiden käyttöönotto

Kun sovellus suoritetaan, Spring-sovelluskehys lataa käyttöönsä sovelluskehyksen tarjoamat apuvälineet. Voimme tuoda apuvälineitä käyttöön sovellukseemme @Autowired-annotaatiolla. Esimerkkiemme kannalta oleellisin apuväline on luokka JdbcTemplate, joka tarjoaa mahdollisuuden tietokantakyselyiden tekemiseen.

Alla olevassa esimerkissä sovelluksen käyttöön on tuotu JdbcTemplate-luokasta luotu olio. Spring luo olion puolestamme ja määrittelee siihen liittyvät asetukset antamamme application.properties-tiedoston perusteella.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jdbc.core.JdbcTemplate;

@SpringBootApplication
public class NimetSpringSovellus implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(NimetSpringSovellus.class);
    }

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Override
    public void run(String... args) throws Exception {
        // Komentorivisovelluksen toiminnallisuus
        System.out.println("Hei maailma!");
    }
}

Tietokantakyselyn suorittaminen

Oletetaan, että käytössämme on edellisestä luvusta tuttu tietokantataulu Opiskelija, jonka sisältö on seuraava.

opiskelijanumero (integer) nimi (varchar) syntymävuosi (integer) pääaine (varchar)
9999999 Pihla 1997 Tietojenkäsittelytiede
9999998 Joni 1993 Tietojenkäsittelytiede
...

Spring-sovelluskehyksen tarjoaman JdbcTemplate-luokan avulla opiskelijoiden nimien tulostaminen tapahtuu seuraavasti.

// importit

@SpringBootApplication
public class NimetSpringSovellus implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(NimetSpringSovellus.class);
    }

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Override
    public void run(String... args) throws Exception {
        // Komentorivisovelluksen toiminnallisuus
        System.out.println("Hei maailma!");

        // Kyselyn suorittaminen ja tulosten listaaminen
        jdbcTemplate.query(
                "SELECT nimi FROM Opiskelija;",
                (rs, rowNum) -> rs.getString("nimi")
        ).forEach(System.out::println);
    }
}

Mikäli tietokantataulussa on yllä kuvatut kaksi riviä, ohjelman tulostus on seuraava:

Esimerkkitulostus

Hei maailma! Joni Pihla

Tarkastellaan kyselyä hieman tarkemmin.

jdbcTemplate.query(
    "SELECT nimi FROM Opiskelija;",
    (rs, rowNum) -> rs.getString("nimi")
).forEach(System.out::println);

Luokan JdbcTemplate metodi query saa parametrinaan SQL-kielisen lauseen sekä lambda-lausekkeen, joka kertoo mitä tulostaulun riveille tulee tehdä. Yllä SQL-kielinen lause on "SELECT nimi FROM Opiskelija", eli hae kaikki Opiskelija-taulussa olevat nimet.

Lambdalauseke (rs, rowNum) -> rs.getString("nimi") sisältää rs-nimisen ResultSet-olion ja kokonaislukutyyppisen rowNum-muuttujan. Kuten JDBC-harjoittelusta tiedämme, ResultSet-olio sisältää kyselyn tulokset, ja siltä voi kysyä sarakkeen arvoja. Muuttuja rowNum on Spring-sovelluskehyksen tarjoama lisätuki, joka kertoo kyseisen rivin. Lauseke muuntaa kunkin tulosrivin merkkijonoksi, joka sisältää rivillä olevan nimen.

Metodi query palauttaa listan, joka käytään lopulta forEach-metodilla läpi.

Kyselyn voisi toteuttaa myös seuraavalla tavalla.

List<String> nimet = jdbcTemplate.query(
    "SELECT nimi FROM Opiskelija;",
    (rs, rowNum) -> rs.getString("nimi"));

nimet.forEach(System.out::println);
Loading

Parametrien lisääminen kyselyyn

Parametrien lisääminen kyselyyn on mahdollista edellisessä luvussa tutuksi tulleella tavalla. Suurin ero edelliseen on se, että aiemmin parametrit lisättiin yksi kerrallaan PreparedStatement-olion tarjoamilla metodeilla. JdbcTemplate-luokkaa käytettäessä parametrit lisätään kyselyn loppuun.

Alla olevassa esimerkissä kyselyyn lisätään rajausehto — haluamme vain Pihla-nimisen opiskelijan tiedot.

List<String> nimet = jdbcTemplate.query(
    "SELECT * FROM Opiskelija WHERE nimi = ?;",
    (rs, rowNum) -> rs.getString("nimi"),
    "Pihla");

Alla olevassa esimerkissä haetaan Opiskelija-taulusta henkilöitä, joiden syntymävuosi on pienempi kuin 2000 ja joiden pääaine on tietojenkässittelytiede.

List<String> nimet = jdbcTemplate.query(
    "SELECT * FROM Opiskelija WHERE syntymävuosi  < ? AND pääaine = ?",
    (rs, rowNum) -> rs.getString("nimi"),
    2000, "Tietojenkäsittelytiede");

Kuten aiemmin, ohjelma voi toimia myös siten, että rajausehdot kysytään ohjelman käyttäjältä.

Scanner lukija = new Scanner(System.in);
System.out.println("Minä vuonna syntyneet opiskelijat tulostetaan?");
int vuosi = Integer.parseInt(lukija.nextLine());

// ...
List<String> nimet = jdbcTemplate.query(
    "SELECT * FROM Opiskelija WHERE syntymävuosi = ?",
    (rs, rowNum) -> rs.getString("nimi"),
    vuosi);
// ...

Tietoa muokkaavien kyselyiden tekeminen

Tietoa muokkaavat kyselyt — kuten tiedon lisäämiskyselyt, muokkauskyselyt, tai tietokannan rakenteeseen vaikuttavat kyselyt — suoritetaan JdbcTemplate-olion update-metodilla.

Alla olevassa esimerkissä luodaan tietokantatauluun Opiskelija uusi rivi. Uuden opiskelijan nimeksi tulee Matti, syntymävuodeksi 1973 ja pääaineeksi Matematiikka.

// ...

jdbcTemplate.update("INSERT INTO Opiskelija (nimi, syntymävuosi, pääaine) VALUES (?, ?, ?)", "Matti", 1973, "Matematiikka");

// ...

Ohjelma voisi sisältää myös Scanner-olion, joka kysyy em. parametreja käyttäjältä. Tällöin sovellus olisi — esimerkiksi — seuraava.

// ...
Scanner lukija = new Scanner(System.in);
System.out.println("Minkä niminen opiskelija lisätään?");
String nimi = lukija.nextLine();
System.out.println("Milloin lisättävä opiskelija on syntynyt?");
int syntymavuosi = Integer.valueOf(lukija.nextLine());
System.out.println("Mikä on lisättävän opiskelijan pääaine?");
String aine = lukija.nextLine();


jdbcTemplate.update("INSERT INTO Opiskelija (nimi, syntymävuosi, pääaine) VALUES (?, ?, ?)", nimi, syntymavuosi, aine);

// ...
Loading
Loading
Pääsit aliluvun loppuun! Jatka tästä seuraavaan osaan: