Utilizzare Spring nelle web application – parte 10

Vediamo in questo appuntamento come modificare la nostra applicazione per fare in modo che utilizzi, per il salvataggio e la lettura dei dati, il database creato nell’articolo precedente.

Configurazione

Per prima cosa dobbiamo creare il file src/main/resources/jdbc.properties con questo contenuto:

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/arteraspringtutorial
jdbc.username=[utente]
jdbc.password=[password]

sostituendo [utente] e [password] con i valori appropriati per il proprio database.

Questo file memorizza i dati di accesso al database, e verrà utilizzato anche dall’applicazione finale; ricordiamo infatti che i file presenti nella directory src/main/resources/ vengono copiati nel classpath, ovvero nella directory /WEB-INF/classes della web application.

Fatto ciò, passiamo a src/main/resources/applicationContext.xml, aggiungendo queste righe:

classpath:jdbc.properties

Alcune doverose considerazioni:

  • il bean con id=”propertyConfigurer” indica a Spring di caricare le properties contenute in jdbc.properties;
  • il bean con id=”dataSource” è un’implementazione di javax.sql.DataSource. L’id “dataSource” è una convenzione di Spring per designare il dataSource dell’applicazione, nel caso ve ne fosse solo uno;
  • il bean con id=”transactionManager” definisce il componente che si occuperà di coordinare le transazioni delle classi annotate come @Service o @Transactional.

Spring JDBC

Questa libreria è già inclusa nelle dipendenze del progetto (spring-jdbc) e contiene diverse classi di utilità che consentono di gestire in maniera molto elegante e affidabile le complesse (e decisamente poco eleganti e usabili) JDBC API, consentendo al programmatore di concentrarsi sulla realizzazione delle query e non su aspetti “burocratici”: apertura/chiusura connessioni e delle transazioni, corretta gestione delle eccezioni, iterazione sui risultati, e molto altro.

org.springframework.jdbc.core.JdbcTemplate è la classe più importante di questo package e come molte delle classi di utilità di Spring utilizza il Template Method design pattern. La documentazione ufficiale di Spring la descrive molto bene (nda: traduzione):

Semplifica l’utilizzo di JDBC e aiuta a evitare gli errori più comuni. Si occupa di gestire correttamente il workflow delle JDBC API, lasciando al codice applicativo il compito di fornire l’SQL e di estrarre i risultati. Questa classe esegue query o update SQL, inizializzando l’iterazione sui ResultSet, catturando le eccezioni JDBC e traducendole nelle corrispettive generiche e più informative definite nel package org.springframework.dao.

Chi ha anche solo un minimo di esperienza con le macchinose JDBC API ha certamente idea di cosa vuol dire gestirne correttamente il workflow e siamo sicuri che apprezzerà le funzionalità fornite da JdbcTemplate!

InsertionDao

Per chi voglia implementare dei DAO utilizzando le JDBC API, Spring mette a disposizione una classe astratta: org.springframework.jdbc.core.support.JdbcDaoSupport. Estendendola, e configurando un dataSource, avremo a disposizione un DAO con un’istanza già pronta e funzionante di JdbcTemplate: nessun bisogno di configurare alcunché, nessun bisogno di specificare transazioni (gestite da Spring), nessuna gestione di eccezioni JDBC. Molto comodo!

Ecco quindi come cambia la nostra implementazione di it.artera.springtut.dao.InsertionDao:

package it.artera.springtut.dao;

import it.artera.springtut.model.Insertion;

import java.util.List;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;

@Repository
public class InsertionDao extends JdbcDaoSupport {

    private RowMapper<?> insertionRowMapper;

    public InsertionDao() {
        this.insertionRowMapper = new InsertionRowMapper();
    }

    @Autowired
    public void init(DataSource dataSource) {
        setDataSource(dataSource);
    }

    /**
     * Carica gli ultimi 5 annunci inseriti nel database
     * @return
     */
    @SuppressWarnings("unchecked")
    public List<Insertion> getMostRecentInsertions() {
        StringBuilder sql = new StringBuilder(255);
        sql.append("select");
        addSelectFields(sql);
        sql.append(" from insertions as i");
        sql.append(" order by i.creationDate desc");
        sql.append(" limit 5");

        return (List<Insertion>) getJdbcTemplate().query(sql.toString(), insertionRowMapper);
    }

    /**
     * Carica l'inserzione con l'ID specificato
     * @param id
     * @return
     */
    public Insertion getInsertion(long id) {
        StringBuilder sql = new StringBuilder(255);
        sql.append("select");
        addSelectFields(sql);
        sql.append(" from insertions as i");
        sql.append(" where id = " + id);

        return (Insertion) getJdbcTemplate().queryForObject(sql.toString(), insertionRowMapper);
    }

    private void addSelectFields(StringBuilder sql) {
        sql.append(" i.id, i.title, i.description, i.photo, i.price, i.creationDate");
    }
}

Da notare:

  • le string SQL vengono create tramite degli StringBuilder per la massima flessibilità;
  • ai metodi query() e queryForObject() di JdbcTemplate viene passata un’istanza di RowMapper (vedi oltre per il sorgente). Questa verrà utilizzata per mappare correttamente i dati contenuti nel ResultSet nelle property delle istanze di oggetti Insertion;

Ecco quindi il sorgente di it.artera.springtut.dao.InsertionRowMapper, l’implementazione di RowMapper specifica per la nostra classe:

package it.artera.springtut.dao;

import it.artera.springtut.model.Insertion;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

/**
 * Utilizzato per mappare le SELECT sql su oggetti Java, l'ordine dei campi
 *  lo stesso presente nella classe java {@link Insertion}
 */
class InsertionRowMapper implements RowMapper<Insertion> {
    public Insertion mapRow(ResultSet rs, int rowNum) throws SQLException {
        Insertion insertion = new Insertion();
        int i = 1;
        insertion.setId(rs.getLong(i++));
        insertion.setTitle(rs.getString(i++));
        insertion.setDescription(rs.getString(i++));
        insertion.setPhoto(rs.getString(i++));
        insertion.setPrice(rs.getBigDecimal(i++));
        insertion.setCreationDate(rs.getDate(i++));

        return insertion;
    }
}

 

Verifica

È arrivato il momento di fare una verifica, possiamo lanciare tutti i test dell’applicazione con il comando:

mvn test

oppure possiamo semplicemente verificare il solo InsertionDao eseguendo la classe di test InsertionDaoTest:

mvn test -Dtest=InsertionDaoTest

Se avete seguito correttamente le istruzioni, dovreste avere un bel messaggio BUILD SUCCESSFUL. Abbiamo infatti modificato solamente l’implementazione del nostro DAO, ma non l’interfaccia; quindi, a rigor di logica, i test (e in generale chi usa il DAO) dovrebbero continuare a funzionare normalmente, senza richiedere alcuna modifica.

Linkografia

Lascia un commento

Tutti i campi sono obbligatori.
L'indirizzo email non verrà pubblicato

 

Commenti

  1. avatarAlessandro

    Come mai non utilizzi hibernate gestire la persistenza?

  2. avatarDimitri De Franciscis Autore

    Inizialmente si pensava di mostrare diversi approcci: JDBC (come ora), Hibernate ed eventualmente MyBatis, ma… sarebbero diventati troppi articoli e troppo lunghi! Abbiamo quindi deciso di rimanere su JDBC e le relative utility di Spring, che comunque fanno la loro bella figura :)

  3. avatarAlessandro

    Ma secondo te cosa è meglio utilizzare?
    O meglio, in base a cosa si deve scegliere se utilizzare hibernate o jdbc?

  4. avatarDimitri De Franciscis Autore

    Per un progetto “vero” utilizzerei quasi di sicuro un ORM, sia Hibernate, MyBatis o un altro; nonostante lo sforzo iniziale di configurazione infatti, i benefici sono davvero molti.

  5. Pingback: Utilizzare Spring nelle web application - parte 14 - blog.artera.net

  6. Pingback: Utilizzare Spring nelle web application - parte 13 - blog.artera.net