In questa Parte 2 del tutorial di Spring3, vengono mostrate alcune classi di esempio con relative annotazioni per ciascuno strato della nostra web application:
- strato controller (Spring MVC): annotazione @Controller
- strato di business logic: annotazione @Service
- strato di persistenza e modellazione dei dati (Spring ORM): annotazioni @Entity e @Repository
Riporto qui di seguito un esempio di model o entity, che mappa una tabella sul database con relative associazioni ad altre entità. Il model viene annotato con @Entity e deve essere enumerato nelle annotatedClass del file spring-orm.xml, visto nella Parte1 di questo tutorial. Per maggiori informazioni, leggere anche Hibernate Annotations.
Nell’esempio sono riportate anche delle annotazioni per la validazione dei campi del model (javax.validation e org.hibernate.validator.constraints), che spostano la validazione lato server verso il lato persistenza e che Spring intercetta e gestisce con opportuni messaggi di stato.
package com.yourDomain.model; import com.yourDomain.interceptors.IAuditLog; import java.io.Serializable; import java.util.Date; import java.util.List; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.NotEmpty; import org.springframework.format.annotation.DateTimeFormat; @Entity @Table(name="utenti") @NamedQueries({ @NamedQuery(name = "Utente.findAll", query = "from Utente where id!=-1 order by cognome"), @NamedQuery(name = "Utente.findById", query = "from Utente r where r.id = :idUtente"), @NamedQuery(name = "Utente.findByCodiceFiscaleAndEmail", query = "from Utente r where lower(r.codiceFiscale) = lower(:codiceFiscale) and lower(r.email) = lower(:email) and dataCancellazione is null and abilitato = true"), @NamedQuery(name = "Utente.findByCodiceFiscaleOrEmail", query = "from Utente r where (lower(r.codiceFiscale) = lower(:codiceFiscale) or lower(r.email) = lower(:email) ) and dataCancellazione is null") }) public class Utente implements Serializable,IAuditLog { private static final long serialVersionUID = 8797826874630455491L; public static final Integer ID_ADMINISTRATOR = -1; @Id @GeneratedValue @Column(name="id") private Integer id; @Column(name="matricola",nullable=true,length=50) private String matricola; @Column(name="cognome",nullable=false,length=255) @NotEmpty @NotNull private String cognome; @Column(name="nome",nullable=false,length=255) @NotEmpty @NotNull private String nome; @ManyToOne(fetch=FetchType.EAGER, targetEntity=StatoCivile.class) @JoinColumn(name = "fk_stato_civile", nullable = true, referencedColumnName = "id") private StatoCivile statoCivile; @Column(name="codice_fiscale",nullable=true,length=16) @NotEmpty @NotNull @Pattern(regexp="^[a-zA-Z]{6}[0-9]{2}[abcdehlmprstABCDEHLMPRST]{1}[0-9]{2}([a-zA-Z]{1}[0-9]{3})[a-zA-Z]{1}$") private String codiceFiscale; @Column(name="data_nascita",nullable=true) @Temporal(TemporalType.DATE) @DateTimeFormat(pattern="dd/MM/yyyy") private Date dataNascita; @ManyToOne(fetch=FetchType.EAGER, targetEntity=Comune.class) @JoinColumn(name = "fk_comune_nascita", nullable = true, referencedColumnName = "id") private Comune comuneNascita; @ManyToOne(fetch=FetchType.EAGER, targetEntity=Stato.class) @JoinColumn(name = "fk_stato_nascita", nullable = true, referencedColumnName = "id") private Stato statoNascita; @Column(name="indirizzo_residenza",length=255,nullable=true) private String indirizzoResidenza; @Column(name="cap_residenza",length=5,nullable=true) @Length(min=0,max=5) @Pattern(regexp="^[0-9]{0,5}") private String capResidenza; @ManyToOne(fetch=FetchType.EAGER, targetEntity=Comune.class) @JoinColumn(name = "fk_comune_residenza", nullable = true, referencedColumnName = "id") private Comune comuneResidenza; @Column(name="email",length=255,nullable=false) @NotEmpty @NotNull @Email private String email; @Column(name="partita_stipendio",length=50,nullable=true) private String partitaStipendio; @Column(name="sesso",length=1,nullable=true) @NotEmpty @NotNull private String sesso; @Column(name="telefono",length=100,nullable=true) //@Pattern(regexp="^(1\\s*[-\\/\\.]?)?(\\((\\d{3})\\)|(\\d{3}))\\s*[-\\/\\.]?\\s*(\\d{3})\\s*[-\\/\\.]?\\s*(\\\\d{4})\\s*(([xX]|[eE][xX][tT])\\.?\\s*(\\d+))*$") private String telefono; @Column(name="fax",length=100,nullable=true) //@Pattern(regexp="^(1\\s*[-\\/\\.]?)?(\\((\\d{3})\\)|(\\d{3}))\\s*[-\\/\\.]?\\s*(\\d{3})\\s*[-\\/\\.]?\\s*(\\\\d{4})\\s*(([xX]|[eE][xX][tT])\\.?\\s*(\\d+))*$") private String fax; @Column(name="cellulare",length=100,nullable=true) //@Pattern(regexp="^(1\\s*[-\\/\\.]?)?(\\((\\d{3})\\)|(\\d{3}))\\s*[-\\/\\.]?\\s*(\\d{3})\\s*[-\\/\\.]?\\s*(\\\\d{4})\\s*(([xX]|[eE][xX][tT])\\.?\\s*(\\d+))*$") private String cellulare; @Column(name="codice_abi",length=100,nullable=true) private String codiceABI; @Column(name="codice_cab",length=100,nullable=true) private String codiceCAB; @Column(name="numero_figli",nullable=true) private Integer numeroFigli; @Column(name="perc_invalidita",length=100,nullable=true) //@Pattern(regexp="^\\d+((\\.|,)\\d+)?$") private String percentualeInvalidita; @ManyToOne(fetch=FetchType.EAGER, targetEntity=AlboProfessionale.class) @JoinColumn(name = "fk_albo_professionale", nullable = true, referencedColumnName = "id") private AlboProfessionale alboProfessionale; @ManyToOne(fetch=FetchType.EAGER, targetEntity=SpecializzazioneProfessionale.class) @JoinColumn(name = "fk_spec_professionale", nullable = true, referencedColumnName = "id") private SpecializzazioneProfessionale specializzazioneProfessionale; @Column(name="data_modifica",nullable=true) @Temporal(TemporalType.DATE) @DateTimeFormat(pattern="dd/MM/yyyy") private Date dataModifica; @Column(name="data_creazione",nullable=true) @Temporal(TemporalType.DATE) @DateTimeFormat(pattern="dd/MM/yyyy") private Date dataCreazione; @Column(name="data_cancellazione",nullable=true) @Temporal(TemporalType.DATE) @DateTimeFormat(pattern="dd/MM/yyyy") private Date dataCancellazione; @Column(name="data_ultimo_accesso",nullable=true) @Temporal(TemporalType.DATE) @DateTimeFormat(pattern="dd/MM/yyyy") private Date dataUltimoAccesso; @ManyToOne(fetch=FetchType.LAZY, targetEntity=Utente.class) @JoinColumn(name = "fk_utente_modifica", nullable = true, referencedColumnName = "id") private Utente utenteModifica; @Column(name="abilitato",nullable=false) private boolean abilitato = true; @ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL,targetEntity=GruppoOperativo.class) @JoinTable(name = "associativa_utenti_gruppi", joinColumns = @JoinColumn(name = "fk_Utente", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "fk_gruppo", referencedColumnName = "id") ) private List<GruppoOperativo> gruppiOperativi; @ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL,targetEntity=TitoloStudio.class) @JoinTable(name = "associativa_utenti_titolistudio", joinColumns = @JoinColumn(name = "fk_Utente", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "fk_titolo_studio", referencedColumnName = "id") ) private Set<TitoloStudio> titoliStudio; @OneToMany(mappedBy = "Utente", cascade = CascadeType.ALL, fetch = FetchType.LAZY, targetEntity=Rapporto.class) private List<Rapporto> rapporti; //@Column(name="nato_estero",nullable=true) @Transient private boolean natoInStatoEstero; @Transient private boolean isLogged; @Transient private String maxVisibility; public Utente(){ } /** QUI TUTTI I METODI DI GET E SET **/ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((id == null) ? 0 : id.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Utente other = (Utente) obj; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; return true; } }
@Controller
Il controller è una interfaccia che riceve una richiesta HTTP – HttpServletRequest e HttpServletResponse – simile alla Action di Struts. Dunque, definisce lo strato di interfaccia tra presentation e business layer. L’annotazione da utilizzare è @Controller e per permettere a Spring MVC di effettuare l’autowiring in fase di startup della web app occorre inserire nel file di configurazione (spring-application.xml) il tag mvc:annotation-driven (come visto nella Parte1). Nel seguente esempio, vengono annotati anche gli editors, che sono utili per popolare dinamicamente select o checkbox lato template. L’annotazione @InitBinder serve per configurare il controller allo startup della web application (in questo caso, a registrare la lista degli Editors). Con @Autowired viene iniettato il servizio che verrà utilizzato dal controller.
Non è necessaria una interfaccia da implementare, ma occorre solo annotare la classe con @Controller, in quanto il controller stesso non viene “iniettato”.
Per maggiori dettagli sui controller e su Spring MVC, leggere questo link: Web MVC Framework.
package com.yourDomain.controller; import com.yourDomain.model.Profilo; import com.yourDomain.model.Rapporto; import com.yourDomain.model.Utente; import java.beans.PropertyEditorSupport; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.validation.Valid; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.SessionAttributes; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; @Controller @RequestMapping("/anagraficautente") @SessionAttributes({"utenteLoggato","utenteSelezionato"}) public class UtenteController { protected static Logger log = Logger.getLogger(UtenteController.class); @Autowired UtenteService UtenteService; @InitBinder public void initBinder(WebDataBinder b) { DateFormat dateFormat = new SimpleDateFormat(SiapConfigProperties.getValue("dateFormat.small")); b.registerCustomEditor(Profilo.class, new ProfiloEditor()); } //EDITOR private class DateEditor extends CustomDateEditor { public DateEditor(DateFormat dateFormat, boolean allowEmpty) { super(dateFormat, allowEmpty); // TODO Auto-generated constructor stub } @Override public void setAsText(String text) throws IllegalArgumentException { try { if(!Utils.isEmpty(text)){ DateFormat df = new SimpleDateFormat(SiapConfigProperties.getValue("dateFormat.small")); setValue(df.parse(text)); } } catch (Exception e) { setValue(null); } } @Override public String getAsText() { if(((Date) getValue()) == null) return ""; DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy"); return ((String) formatter.format(getValue())); } } //EDITOR private class ProfiloEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { try { if(!Utils.isEmpty(text)){ Profilo profilo = profiloService.getProfilo(Integer.valueOf(text)); if(profilo.getId()==null)setValue(null); else setValue(profilo); } } catch (Exception e) { setValue(null); } } @Override public String getAsText() { if((Profilo) getValue() == null ||((Profilo) getValue()).getId()==null) return ""; String id = ((Profilo) getValue()).getId().toString(); return id; } } @RequestMapping(value = "/utente", method = {RequestMethod.GET,RequestMethod.POST}) public ModelAndView redirectToAnagraficaUtente(@RequestParam(value = "id", required = false) String id, HttpSession session, ModelAndView mav) { try{ Utente Utente = null; if(id == null && session.getAttribute("UtenteSelezionata") != null){ Utente = (Utente)session.getAttribute("UtenteSelezionata"); }else if(id != null){ if(id != ""){ Utente = UtenteService.getUtente(Integer.parseInt(id)); }else if(id == ""){ Utente = (Utente) session.getAttribute("utenteLoggato"); } mav.addObject("UtenteSelezionata", Utente); } if(Utente == null){ mav.addObject("result", ResultBean.writeMessageByKey(ResultBean.ERROR, "Utente.storico.error")); mav.setViewName(MappingMavController.STORICO_CARRIERA_JSP); } mav.setViewName("/anagraficaUtente"); } catch (Exception e) { mav.addObject("result", ResultBean.writeMessageByKey(ResultBean.ERROR, "error.generic")); log.error("UtenteController - metodo redirectToAnagraficaUtente: ",e); } return mav; } }
@Service
Con l’annotazione @Service viene definita la classe di servizio con la logica di business, iniettata nel controller o in altri strati della nostra web application. Tale classe implementa una interfaccia. In tale strato è possibile definire la gestione delle transazioni, sia mappando le regole sul file spring-orm.xml, come visto nella Parte1, che inserendo sulla classe di servizio (gestione globale della transazione) e sui singoli metodi (gestione locale della transazione) l’annotazione @Transactional (definendo il tipo di propagazione, come avviene negli EJB). In tale classe vengono “iniettati” anche i DAO per la comunicazione con il database o altre classi di servizio (annotazione @Autowired).
package com.yourDomain.service; import com.yourDomain.model.Utente; import java.util.Map; public interface UtenteService { Utente getUtenteByCodiceFiscaleAndEmail (String codiceFiscale, String email) throws Exception; boolean updateUtente (Utente Utente) throws Exception; public Map<String,String> getContestiAndOperazioniUtente (Utente Utente) throws Exception; }
package it.beniculturali.siapweb.service.impl; import it.beniculturali.siapweb.dao.ContestoDao; import it.beniculturali.siapweb.dao.UtenteDao; import it.beniculturali.siapweb.model.Contesto; import it.beniculturali.siapweb.model.Utente; import it.beniculturali.siapweb.service.UtenteService; import it.beniculturali.siapweb.utilities.Utils; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service("utenteService") @Transactional(propagation=Propagation.REQUIRED, readOnly=true) public class UtenteServiceImpl implements UtenteService { protected static Logger log = Logger.getLogger(UtenteServiceImpl.class); public UtenteServiceImpl(){ } @Autowired private ContestoDao contestoDao; @Autowired private UtenteDao utenteDao; @Override /** * metodo che ritorna la utente con un dato codice fiscale e una email * * @param codiceFiscale, email * @return Utente * */ public Utente getUtenteByCodiceFiscaleAndEmail (String codiceFiscale, String email) throws Exception{ log.debug("UtenteServiceImpl - getUtenteByCodiceFiscaleAndEmail: inizio metodo"); Utente utente = utenteDao.getUtenteByCodiceFiscaleAndEmail(codiceFiscale, email); log.debug("UtenteServiceImpl - getUtenteByCodiceFiscaleAndEmail: fine metodo"); return utente; } /** * metodo che aggiorna i dati di una determinata utente * * @param codiceFiscale, email * @return Utente * */ @Override @Transactional(propagation=Propagation.REQUIRED, readOnly=false) public boolean updateUtente(Utente utente) throws Exception { log.debug("UtenteServiceImpl - updateUtente: inizio metodo"); boolean result = false; log.debug("Aggiornamento dei dati della utente con id."+utente.getId()); result = utenteDao.updateUtente(utente); log.debug("UtenteServiceImpl - updateUtente: fine metodo"); return result; } /** * metodo che ritorna la mappa dei contesti di un utente con relative operazioni permesse * * @param utente * @return * @throws Exception */ @Override public Map<String,String> getContestiAndOperazioniUtente(Utente utente) throws Exception { Map<String,String> contestiUtenteMap = new HashMap<String, String>(); log.debug("UtenteServiceImpl - getContestiAndOperazioniUtente: inizio metodo"); try{ List<Contesto> contestiUtente = contestoDao.getContestiAndOperazioniUtente(utente); //costruisco la lista delle operazioni per ogni contesto if(contestiUtente!=null){ for(Contesto contestoUtente : contestiUtente){ //costruisco la mappa con le operazioni permesse dell'utente loggato contestiUtenteMap.put(contestoUtente.getId().toString(), Utils.convertListStringInSingleString(contestoUtente.getTipiOperazioniUtente(),",")); } } } catch(Exception e){ log.error("ContestoDaoImpl - getContestiAndOperazioniUtente: ",e); } log.debug("UtenteServiceImpl - getContestiAndOperazioniUtente: fine metodo"); return contestiUtenteMap; } }
@Repository
L’annotazione @Repository definisce le classi dello strato di persistenza, ossia i cosiddetti DAO (Data Access Object). Anche qui le classi implementano una interfaccia, visto che verranno “iniettate” nello strato di servizio (con annotazione @Autowired). Qui viene iniettato anche il SessionFactory (o l’HibernateTemplate), il manager che ci permetterà di eseguire le operazioni CRUD sulla basedati definita e configurata nella Parte1 di questo tutorial.
package com.yourDomain.dao; import com.yourDomain.model.GruppoOperativo; import com.yourDomain.model.Utente; import java.util.List; public interface UtenteDao { List<Utente> getUtenti() throws Exception; Utente getUtenteByCodiceFiscaleAndEmail (String codiceFiscale, String email) throws Exception; boolean isUtenteRegistrato (String codiceFiscale, String email, Integer idUtente) throws Exception; Utente getUtente (Integer idUtente) throws Exception; public int countUtentiForGruppoOperativo (GruppoOperativo gruppoOperativo) throws Exception; boolean saveUtente(Utente nuovoUtente) throws Exception; boolean updateUtente(Utente risorsa) throws Exception; }
package com.yourDomain.dao.impl; import com.yourDomain.dao.UtenteDao; import com.yourDomain.model.GruppoOperativo; import com.yourDomain.model.Utente; import java.util.Date; import java.util.List; import org.apache.log4j.Logger; import org.hibernate.Criteria; import org.hibernate.FetchMode; import org.hibernate.Hibernate; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.criterion.CriteriaSpecification; import org.hibernate.criterion.Criterion; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Restrictions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Repository("utenteDao") @Transactional(propagation = Propagation.SUPPORTS, readOnly = true) public class UtenteDaoImpl implements UtenteDao { @Autowired SessionFactory sessionFactory; protected static Logger log = Logger.getLogger(UtenteDaoImpl.class); public UtenteDaoImpl() { } /** * metodo che ritorna la lista degli utenti * * @return List<Utente> */ @SuppressWarnings("unchecked") @Override public List<Utente> getUtenti() { Session session = sessionFactory.getCurrentSession(); Query query = session.getNamedQuery("Utente.findAll"); return query.list(); } /** * metodo che ritorna l'utente con un dato codice fiscale e una email * * @param codiceFiscale * , email * @return Utente * */ @Override public Utente getUtenteByCodiceFiscaleAndEmail (String codiceFiscale, String email) throws Exception{ Session session = sessionFactory.getCurrentSession(); Query query = session.getNamedQuery("Utente.findByCodiceFiscaleAndEmail") .setString("codiceFiscale", codiceFiscale) .setString("email", email); return (Utente)query.uniqueResult(); } /** * metodo che ci dice se c'è un utente registrato con un dato codice fiscale o email * * @param codiceFiscale * , email * @return Utente * */ @SuppressWarnings("unchecked") @Override public boolean isUtenteRegistrato (String codiceFiscale, String email, Integer idUtente) throws Exception{ Session session = sessionFactory.getCurrentSession(); Criteria criteria = session.createCriteria(Utente.class); criteria.add(Restrictions.isNull("dataCancellazione")); Criterion criterion1 = Restrictions.eq("codiceFiscale", codiceFiscale).ignoreCase(); Criterion criterion2 = Restrictions.eq("email", email).ignoreCase(); Criterion ORcriterion = Restrictions.or(criterion1, criterion2); criteria.add(ORcriterion); if(idUtente!=null){ criteria.add(Restrictions.ne("id",idUtente)); } List<Utente> utentiRegistrati = criteria.list(); return utentiRegistrati!=null&&utentiRegistrati.size()!=0?true:false; } /** * metodo che ritorna un utente corrispondente ad un determinato id * * @param idUtente * */ @Override public Utente getUtente(Integer idUtente) throws Exception { Session session = sessionFactory.getCurrentSession(); Query query = session.getNamedQuery("Utente.findById") .setInteger("idUtente", idUtente); return (Utente)query.uniqueResult(); } /** * metodo che conta quanti utenti sono associati a quel gruppo operativo * * @param gruppoOperativo * @return * @throws Exception */ @Override public int countUtentiForGruppoOperativo (GruppoOperativo gruppoOperativo) throws Exception{ Session session = sessionFactory.getCurrentSession(); Criteria criteria = session.createCriteria(Utente.class) .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) .setFetchMode("gruppiOperativi", FetchMode.JOIN); criteria.createAlias("gruppiOperativi", "g",CriteriaSpecification.INNER_JOIN); criteria.add(Restrictions.eq("g.id", gruppoOperativo.getId())); criteria.add(Restrictions.isNull("dataCancellazione")); Number count = (Number) criteria.setProjection(Projections.rowCount()).uniqueResult(); return count!=null?count.intValue():0; } /** * metodo che aggiorna i dati di una determinata utente * * @param idUtente * */ @Override @Transactional(propagation=Propagation.REQUIRED, readOnly=false) public boolean updateUtente(Utente utente) throws Exception { try{ Session session = sessionFactory.getCurrentSession(); utente.setDataModifica(new Date()); session.clear(); session.update(utente); session.flush(); //session.evict(utente); } catch(Exception e){ log.error("UtenteDaoImpl - updateUtente: ",e); return false; } return true; } /** * metodo per il salvataggio di una nuova utente * * @param utente * @return * @throws Exception */ @Override @Transactional(propagation=Propagation.REQUIRED, readOnly=false) public boolean saveUtente(Utente nuovoUtente) throws Exception { try{ Session session = sessionFactory.getCurrentSession(); session.clear(); session.save(nuovoUtente); session.flush(); //session.evict(nuovoUtente); } catch(Exception e){ log.error("UtenteDaoImpl - saveUtente: ",e); return false; } return true; } }

This work by Francesco Ficetola is licensed under a Creative Commons Attribution 4.0 International License.
Based on a work at www.francescoficetola.it.
Permissions beyond the scope of this license may be available at http://www.francescoficetola.it/2012/05/16/spring-spring3-dai-controller-ai-repository-parte2/.