Utilizzare Spring nelle web application – parte 14

Nello scorso articolo abbiamo introdotto l’utilizzo della classe SqlUpdate di Spring, creando due sotto-classi specializzate nelle operazioni di INSERT e UPDATE:

  • it.artera.springtut.dao.SqlInsertInsertion per l’operazione di INSERT;
  • it.artera.springtut.dao.SqlUpdateInsertion per l’operazione di UPDATE.

Modifiche a InsertionDao

Dobbiamo ora realizzare il codice che ci permette di salvare il nostro oggetto Insertion, che per comodità sarà in un unico metodo saveOrUpdate(), il cui funzionamento sarà il seguente:

  • se insertion.id è nullo, esegui una INSERT;
  • altrimenti esegui una UPDATE.

Ed ecco il metodo in questione:

public Insertion saveOrUpdate(Insertion insertion) {
    if (insertion.getId() == null) {
        // insert
        sqlInsertInsertion.execute(insertion);
    } else {
        // update
        sqlUpdateInsertion.execute(insertion);
    }

    return insertion;
}

Come si può notare il codice è decisamente… sintetico! Quando nello scorso articolo abbiamo accennato al fatto che realizzare delle sotto-classi ci permette di isolare la complessità, ci riferivamo proprio a questo: l’SQL, la configurazione dei parametri e l’esecuzione della query sono racchiusi all’interno delle classi SqlInsertInsertion e SqlUpdateInsertion, mentre InsertionDao si limita a invocarne il metodo execute().

 

Test JUnit e transazioni

Come d’abitudine, tutti i metodi delle nostre classi (in special modo quelli che “fanno qualcosa”) vanno verificati con un test JUnit, ovviamente saveOrUpdate() non fa eccezione. A questo punto però dobbiamo introdurre una modifica nelle nostre utility di test, ovvero faremo in modo che

  • ogni test venga eseguito in transazione separata;
  • alla fine di ogni test venga fatto un rollback, in modo che eventuali dati modificati dal test siano ripristinati.

Per fare ciò è sufficiente modificare la classe it.artera.springtut.AbstractTest in modo che non estenda più AbstractJUnit4SpringContextTests ma AbstractTransactionalJUnit4SpringContextTests, che offre le stesse funzionalità ma in più è transazionale (come il nome suggerisce):

package it.artera.springtut;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;

@ContextConfiguration(locations = "classpath:applicationContext.xml")
public abstract class AbstractTest extends AbstractTransactionalJUnit4SpringContextTests {
}

Realizziamo ora il codice di test per lo use case di INSERT:

@Test
public void test_saveOrUpdate_insert() {
    Insertion i;

    try {
        // dati incompleti
        i = new Insertion();
        insertionDao.saveOrUpdate(i);
        fail("should throw exception");
    } catch (DataAccessException dae) {
        // OK
    } catch (Exception ex) {
        fail("should throw DataAccessException");
    }

    i = new Insertion();
    i.setTitle("bla bla bla");
    i.setPrice(new BigDecimal("100.00"));
    i.setCreationDate(new Date());
    insertionDao.saveOrUpdate(i);
    assertNotNull(i.getId());
}

Per essere sicuri che funzioni tutto come previsto abbiamo anche verificato che, in caso di dati incompleti, venga lanciata un’eccezione di tipo org.springframework.dao.DataAccessException. Spring “magicamente” cattura le eccezioni SQL e le traduce in una appropriata sotto classe di org.springframework.dao.DataAccessException, questo perché abbiamo annotato InsertionDao con l’annotazione @Repository.

 

Ecco invece il codice per l’UPDATE:

@Test
public void test_saveOrUpdate_update() {
    Insertion i = insertionDao.getInsertion(1L);
    assertEquals("Chitarra acustica", i.getTitle());
    assertEquals(new BigDecimal("50.00"), i.getPrice());
    i.setTitle("bla bla bla");
    i.setPrice(new BigDecimal("100.00"));
    insertionDao.saveOrUpdate(i);

    JdbcTemplate jdbcTemplate = getJdbcTemplate();
    Map<String, Object> result = simpleJdbcTemplate.queryForMap(
        "select i.title, i.price from insertions i where id = 1");
    assertEquals("bla bla bla", result.get("title"));
    assertEquals(new BigDecimal("100.00"), result.get("price"));
}

In questo caso verifichiamo che le modifiche apportate siano avvenute con successo, eseguendo il metodo InsertionDao.saveOrUpdate() e verificando con una query SQL l’avvenuto aggiornamento. La query SQL viene fatta utilizzando simpleJdbcTemplate, un’istanza di org.springframework.jdbc.core.simple.SimpleJdbcTemplate fornita dalla classe base AbstractTransactionalJUnit4SpringContextTests.

 

Esecuzione dei test

Con il comando

mvn test -Dtest=InsertionDaoTest

Maven eseguirà tutti i test di InsertionDaoTest, ma le modifiche sul database non saranno visibili perché dopo ogni test, andato a buon fine o no, la transazione andrà in rollback. In caso contrario dovremmo re-inserire tutti i dati prima di ogni test, sarebbe decisamente scomodo oltre che una perdita di tempo!

 

Progetto completo

Questa serie di articoli per il momento termina qui. Abbiamo percorso molta strada: siamo partiti da un’idea, abbiamo immaginato la nostra applicazione, l’abbiamo realizzata. È arrivato il momento di sperimentare, per facilitarvi abbiamo pensato di allegare il progetto completo così com’è allo stato attuale, in modo da non dover ricreare passo per passo tutto quanto.

spring_3_tutorial_20110713.zip

Linkografia

  • JDBC Technology – official documentation (Oracle – ex Sun);
  • Data Access Objects – DAO (Wikipedia);