sabato 25 ottobre 2014

Introduzione a Spring con esempio pratico

Vai all'indice

Questo post, ha l'obiettivo di introdurre Spring, attualmente il Framework più usato per lo sviluppo di applicazioni Java Enterprise.
Per fare questo integrerò Spring nello stesso esempio sviluppato nei primi post che trattavano il Framework Hibernate:

Hibernate relazione uno a uno
Hibernate relazione uno a molti
Hibernate relazione molti a molti

Cercherò di mantenere una struttura dell'applicazione simile e utilizzerò lo stesso database Apache Derby descritto negli esempi appena citati,  in modo che sia chiara la differenza dello sviluppo  con e senza l'utilizzo di Spring. La figura seguente descrive la struttura del DB:


Come prima cosa creo un nuovo progetto Maven utilizzando l'archetipo: maven-archetype-quickstart, Riporto di seguito le dipendenze del POM.xml:


<dependency>
 <groupid>org.hibernate</groupid>
 <artifactid>hibernate-entitymanager</artifactid>
 <version>4.2.0.Final</version>
</dependency>
<dependency>
 <groupid>org.springframework</groupid>
 <artifactid>spring-core</artifactid>
 <version>3.2.2.RELEASE</version>
</dependency>
<dependency>
 <groupid>org.springframework</groupid>
 <artifactid>spring-context</artifactid>
 <version>3.2.2.RELEASE</version>
</dependency>
<dependency>
 <groupid>org.springframework</groupid>
 <artifactid>spring-orm</artifactid>
 <version>3.2.2.RELEASE</version>
</dependency>
<dependency>
 <groupid>org.apache.derby</groupid>
 <artifactid>derbyclient</artifactid>
 <version>10.10.1.1</version>
</dependency>
<dependency>
 <groupid>junit</groupid>
 <artifactid>junit</artifactid>
 <version>3.8.1</version>
 <scope>test</scope>
</dependency>

Dato che il DB è lo stesso e l'applicazione usa Hibernate, le entità utilizzate rimangono le medesime degli esempi sopra citati, per questo motivo riporto solo i nomi delle classi, chi desidera può scaricarle da GitHub:

Utente.java
Dettaglioutente.java
Ufficio.java
Applicazione.java


Passo ora a definire le classi DAO (Data Access Object), ovvero le classi che eseguono l'accesso ai dati del database con operazioni di CRUD (Inserimento, cancellazione, aggiornamento).
Per implementare il DAO ho bisogno dell'EntityManager, che viene iniettato direttamente dall' IoC attraverso l'uso della annotation @PersistenceContext. L’EntityManager ha il compito di tradurre le entità in record nel database. Se richiediamo l’aggiornamento di un'entità, l'EntityManager modifica i record corrispondenti del DB, se invece ne richiediamo la cancellazione provvede a rimuoverli. Viceversa quando richiamiamo un record presente nel database, l'EntityManager crea un oggetto entity bean, lo popola con i dati relazionali e lo restituisce.
Il Persistence Context gestisce l'insieme di tutte le modifiche e i cambiamenti di stato occorsi sugli oggetti entità invocate tramite EntityManager in un dato intervallo di tempo. Le classi DAO usano l'annotazione JPA @Transactional, in questo modo i metodi censiti all'interno di essa eseguiranno un commit sul database se tutte le istruzioni vanno a buon fine, in caso contrario verrà eseguito un rollback. Per attivare il comportamento transazionale è necessario inserire l’elemento <tx:annotation-driven transaction-manager="transactionManager"/> all'interno del file di applicationcontext.xml che vedremo più avanti.
Riporto i tre servizi per la gestione delle operazioni sulle entità.


package it.test.dao;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.transaction.annotation.Transactional;

import it.test.entity.*;


/**
 * Implementazione servizio DAO su Ufficio
 *  
 * @author Mauro Cognolato
 */
@Transactional
public class UfficioDaoImpl {

@PersistenceContext
private EntityManager em;


public int save(Ufficio ufficio) {
em.persist(ufficio);
return ufficio.getUfficioId();
}

public void deleteUfficio(Integer id) {
em.remove(getUfficio(id));
}

public Ufficio getUfficio(Integer id) {
return em.find(Ufficio.class, id);
}

public void aggUffUte(Utente ute, Ufficio uff) {
        uff.getUtenti().add(ute);
        em.merge(uff);
}

public List<Ufficio>getAll() {
return em.createQuery("SELECT p FROM Ufficio p", Ufficio.class).getResultList();
}

}



package it.test.dao;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.transaction.annotation.Transactional;


import it.test.entity.*;

/**
 * Implementazione servizio DAO su Utente
 *  
 * @author Mauro Cognolato
 */
@Transactional
public class UtenteDaoImpl {

@PersistenceContext
private EntityManager em;


public int save(Utente utente) {
em.persist(utente);
return utente.getuserId();
}

public void deleteUtente(Integer id) {
em.remove(getUtente(id));
}

public Utente getUtente(Integer id) {
return em.find(Utente.class, id);
}

public void aggUte(Utente ute) {
        em.merge(ute);
}

public List<Utente>getAll() {
return em.createQuery("SELECT p FROM Utente p", Utente.class).getResultList();
}

public void saveDetUte(Utente ute, Dettaglioutente dettUte) {
ute.setDettaglioutente(dettUte);
dettUte.setUtente(ute);
em.persist(ute);
em.flush();
// riallinea l'entitࡡi valori del DB
em.refresh(ute);
}

}



package it.test.dao; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.springframework.transaction.annotation.Transactional; import it.test.entity.Applicazione; /** * Implementazione servizio DAO su Applicazioni * * @author Mauro Cognolato */
@Transactional public class ApplicazioneDaoImpl { @PersistenceContext private EntityManager em; public int save(Applicazione applicazione) { em.persist(applicazione); em.flush(); em.refresh(applicazione); return applicazione.getAppid(); } public void deleteApplicazione(Integer id) { em.remove(getApplicazione(id)); } public void deleteApplicazioni() { List<Applicazione> appes = getAll(); for (Applicazione appe : appes) { em.remove(appe); } } public Applicazione getApplicazione(Integer id) { return em.find(Applicazione.class, id); } public List<Applicazione>getAll() { return em.createQuery("SELECT p FROM Applicazione p", Applicazione.class).getResultList(); } }


Passiamo ai file di configurazione di Spring. Il persistence.xml definisce le persistence-unit, ovvero l'insieme di tutte le classi entità di un singolo Datasource che sono gestite dall'EntityManager.


<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

<persistence-unit name="jpaData" />

</persistence>

ApplicationContext.xml è il principale file di configurazione di Spring e contiene tutte le specifiche dell'applicazione, analizziamo i bean dichiarati: come prima cosa notiamo la referenziazione delle classi DAO appena definite.
Nell'elemento DataSource sono definiti i parametri di connessione al database Derby. In questo esempio sono in chiaro, ma solitamente i valori vengono gestiti in un file di properties.
L'entityManagerFactory è responsabile della creazione delle istanze dell'EntityManager. Essa richiama la classe LocalContainerEntityManagerFactoryBean che viene configurata per la gestione di JPA e Hibernate. Tra i parametri possiamo notare la definizione della persistent-unit del file persistence.xml visto in precedenza. Tramite l'entityManagerFactory appena creato viene definito anche il transactionManager che coordina la transazionalità delle risorse.



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">

<tx:annotation-driven />
<context:annotation-config />

<bean id="utenteDao" class="it.test.dao.UtenteDaoImpl" />
<bean id="ufficioDao" class="it.test.dao.UfficioDaoImpl" />
<bean id="applicazioneDao" class="it.test.dao.ApplicazioneDaoImpl" />

<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>org.apache.derby.jdbc.ClientDriver</value>
<!-- value>org.apache.derby.jdbc.EmbeddedDriver</value -->
</property>
        <property name="url">
            <value>jdbc:derby://localhost:1527/derby/derbyDB</value>
        </property>
        <property name="username">
            <value>tst</value>
        </property>
        <property name="password">
            <value>test</value>
        </property>
</bean>

<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="jpaData" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.DerbyDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">false</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

</beans>


In conclusione creiamo la classe app che contiene il metodo main per eseguire e testare l'applicazione. Come prima cosa istanzio i servizi Dao, procedo quindi con la definizione degli oggetti entità e la popolazione del database, quindi stampo a video alcuni campi per sincerarmi che le operazioni siano andate a buon fine.

L'utente Rossi Mario vive a Milano e lavora nell'ufficio Vendite
  Abilitato all'applicazione: GIS - Sistema territoriale
  Abilitato all'applicazione: SAP - Sistema gestionale


Come ultimo step ripulisco il database, da notare che eliminando l'oggetto ufficio di conseguenza vengono eliminati tutti gli utenti associati per i vincoli di constraint.


package it.test;

import java.sql.Date;
import java.util.List;
import java.util.Set;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import it.test.dao.UtenteDaoImpl;
import it.test.dao.UfficioDaoImpl;
import it.test.dao.ApplicazioneDaoImpl;
import it.test.entity.Ufficio;
import it.test.entity.Utente;
import it.test.entity.Applicazione;
import it.test.entity.Dettaglioutente;

/**
 * Metodo per testare l'applicazione
 * 
 * @author Mauro Cognolato
 */
public class App {

    public static void main(String[] args) {
    ClassPathXmlApplicationContext context = new  
     ClassPathXmlApplicationContext("applicationContext.xml");
   
    //Istanzio le classi DAO
    UtenteDaoImpl daoUte = (UtenteDaoImpl) context.getBean("utenteDao");
    UfficioDaoImpl daoUff = (UfficioDaoImpl) context.getBean("ufficioDao");
    ApplicazioneDaoImpl daoApp = (ApplicazioneDaoImpl) context.getBean("applicazioneDao");

    // Definisco un oggetto Ufficio e gli assegno il valore 
    Ufficio uff = new Ufficio();
    uff.setNomeUfficio("Vendite");
    daoUff.save(uff);
    int idUff = uff.getUfficioId();
   
    // Istanzio e salvo gli oggetti utente e dettaglioutente
        Dettaglioutente dettUte = new Dettaglioutente( new Date(821212), "via Roma 11", "Milano", "0612345678");
        Utente ute = new Utente("Mario", "Rossi", "Amministratore");
        daoUte.saveDetUte(ute, dettUte);
        
        // Creo gli oggetti Applicazione
        Applicazione app1 = new Applicazione("SAP", "Sistema gestionale");
        daoApp.save(app1);
        Applicazione app2 = new Applicazione("GIS", "Sistema territoriale");
        daoApp.save(app2);

        // Creo un collegamento tra il nuovo utente e l'ufficio
        daoUff.aggUffUte(ute, uff);
        
        // Creo un collegamento tra il nuovo utente e la applicazioni create
        ute.getApplicazioni().add(app1);
        ute.getApplicazioni().add(app2);
        
        // Aggiorno l'utente
        daoUte.aggUte(ute);

        // Recupero e stampo a video gli oggetti
        List<Utente> utenti = daoUte.getAll();
        String nomeUff = uff.getNomeUfficio();
        for (Utente utente : utenti) {
System.out.println("L utente " + utente.getCognome() + " " + utente.getNome() + " vive a " + utente.getDettaglioutente().getCitta() + " e lavora nell'ufficio " + nomeUff);
       for (Applicazione appx : utente.getApplicazioni()) {
        System.out.println("Abilitato all'applicazione: " + appx.getNomeapp() + " - " + appx.getDescrizioneapp());
       }
}
        
        // Pulisco il Database
        // Eliminando l'oggetto uff elimino anche tutti i dettagli associati
        daoUff.deleteUfficio(idUff);
        // Elimino le istanze delle applicazioni
        daoApp.deleteApplicazioni();
        
    }
    
}


In questo post ho introdotto l'utilizzo del framework Spring. Nel prossimo esempio inseriremo un framework di presentazione per la visualizzazione e manipolazione dei dati e di conseguenza trasformeremo l'applicazione in web application.
Potete consultare l'intero progetto su GitHub

Hai apprezzato questo post? Conferma le mie competenze o scrivi una segnalazione sul mio profilo Linkedin!