Guida minimale EJB3 (Seconda Parte)

From Servizi RSI
Jump to: navigation, search

Guida minimale all’uso della persistenza in EJB3 (parte 2)

Questa guida è stata prodotta da: Aurelio D’Amico il 9 Luglio 2007

Prerequisiti

Conoscenza di J2EE versioni precedenti. In particolare: concetto di interfaccia remota e locale; concetto di Session Bean (stateless e stateful); nozioni di SQL.

Session Bean

Andremo ora a sviluppare un Session Bean da utilizzare come servizio per la gestione dell’entità Ruolo. Come nelle versioni precedenti di EJB continuiamo ad aver bisogno di creare un’interfaccia che definisca i metodi da esporre come servizi pubblici. L’interfaccia dovrà essere locale o remota. Un Session Bean può implementare entrambe le interfacce.

RuoloService.java

Definiamo quindi una interfaccia locale con alcune operazioni gestionali:


@Local 
public interface RuoloService { 
  public void createRuolo(Ruolo ruolo); 
  public Ruolo readRuolo(Integer ruoloId); 
  public Ruolo updateRuolo(Ruolo ruolo); 
  public void deleteRuolo(Integer ruoloId); 
  public List findAllRuoli(); 
  public Long countRuoli(); 
} 

L’annotazione @Local in testa alla classe sta ad indicare al server che questa è una interfaccia locale. Utilizzeremmo l’annotazione @Remote se invece volessimo definire una intercaccia remota. A questo punto saremmo in grado facilmente di implementare le prime quattro operazioni CRUD dell’interfaccia, ma per l’implementazione degli ultimi due metodi dobbiamo prima capire come usare il linguaggio EJB QL.

Java Persistence Query Language

EJB3 mette a disposizione un linguaggio proprietario che è possibile utilizzare per interrogare e modificare il database. Il linguaggio è molto simile ad SQL con la differenza sostanziale che si referenziano classi ed attributi piuttosto che tabelle e colonne. Per il resto i comandi SQL sono gli stessi. Perché allora non usare SQL vi domanderete. Lo scopo di EJB3 QL è quello di rendere il codice ignaro del database che si sta utilizzando ed in particolare del suo dialetto SQL. Se siamo attenti e utilizziamo il linguaggio EJB3 QL non saremo “legati” a nessun particolare fornitore di database.

La più semplice forma di query è la seguente:

select x from Ruolo x 

La query ritornerebbe l’elenco di tutti i ruoli nel database. Se invece volessimo accedere per esempio al ruolo 1 scriveremmo:

select x from Ruolo x where x.ruoloId = 1

Si può subito notare la somiglianza con l’uso degli alias in SQL. In questo caso però il case di Ruolo e di ruoloId è determinante.

Implementazione delle query

Troveremo i metodi di generazione delle query nell’oggetto EntityManager. In particolare i metodi createQuery, createNamedQuery e createNativeQuery ciascuno dei quali ritorna un oggetto di tipo Query. Abbiamo ora tutte le nozioni per implementare il nostro Session Bean.

RuoloServiceBean.java

@Stateless 
public class RuoloServiceBean 
extends AbstractManager implements RuoloService { 
  public void createRuolo(Ruolo ruolo) { 
    manager.persist(ruolo); 
  } 
  public Ruolo readRuolo(Integer ruoloId) { 
    return manager.find(Ruolo.class, ruoloId); 
  } 
  public Ruolo updateRuolo(Ruolo ruolo) { 
    return manager.merge(ruolo); 
  } 
  public void deleteRuolo(Integer ruoloId) { 
    manager.remove(ruolo); 
  } 
  public List findAllRuoli() { 
    Query query = manager.createQuery("select x from Ruoli x"); 
    return query.getResultList(); 
  } 
  public Long countRuoli() { 
    Query query = 
    manager.createQuery("select count(x.ruoloId) from Ruoli x"); 
    return (Long) query.getSingleResult(); 
  } 
} 

L’annotazione @Stateless sta ad indicare che si tratta di un Session Bean stateless; va da se che se desiderassimo implementare invece un Session Bean stateful l’annotazione da usare sarebbe @Stateful. Notare che laddove si prevedono più risultati si usa query.getResultList() mentre laddove si prevede un singolo risultato si usa query.getSingleResult().

Query parametrizzate

Utilizzando quelli che si chiamano “parametri di sostituzione” è possibile parametrizzare una query. I parametri di sostituzione si indicano con una stringa preceduta da “:” o con un numero preceduto da “?”. La prima forma è quella consigliata perchè più leggibile e descrittiva. Esempi:

public Ruolo findRuoloById(Integer ruoloId) { 
  Query query = 
    manager.createQuery(“select x from Ruolo x where x.ruoloId =:id”); 
  query.setParameter("id", ruoloId); 
  return (Ruolo) query.getSingleResult(); 
} 
public Ruolo findRuoloById(Integer ruoloId) { 
  Query query = 
    manager.createQuery(“select x from Ruolo x where x.ruoloId =?1”); 
  query.setParameter(1, ruoloId); 
  return (Ruolo) query.getSingleResult(); 
} 

Named Query

Le named query corrispondono ai prepared statement di JDBC. Esse vengono precompilate e sono più performanti. Le named query si definiscono all’interno di un Entity Bean prima della definizione della classe. Non è necessario che la query sia pertinente con l’ Entity Bean. Per esempio tutte le named query usate potrebbero essere centralizzate in un unico Entity Bean.

@NamedQueries({ 
   @NamedQuery(name="findAllRuoli", query="SELECT x FROM Ruoli x”) 
}) 
@Entity 
public class Ruolo {...} 

@Stateless 
public class RuoloServiceBean {... 
  public List findAllRuoli() { 
    Query query = manager.createNamedQuery(“findAllRuoli”); 
    return query. getResultList (); 
  } 
} 

Casi eccezionali

In casi disperati è possibile rompere il contratto di standardizzazione offerto dalla piattaforma EJB3. Il primo meccanismo a disposizione è quello di utilizzare le query native; il secondo è ottenere dal server il datasource sul quale lavorare con i comandi JDBC. Nessuno dei due meccanismi è consigliato poiché ci si andrebbe a “legare” con lo specifico fornitore di database e il suo dialetto SQL.

Query Native

Attraverso una query ottenuta con il comando EntityManager.createNativeQuery è possibile far eseguire al framework di persistenza qualsiasi commando SQL che non sia di tipo DDL. Per esempio l’istruzione query.createNativeQuery(“SELECT * FROM RUOLI”).getResultList(); ritornerebbe una lista di righe in formato di valori scalari (Object[]).

JDBC

Infine è possibile ricorrere a JDBC per i casi veramente atipici come quello per esempio di aggiornare una colonna di tipo BLOB utilizzando il driver nativo fornito dal fornitore del database. Inoltre se si avesse la necessità di eseguire comandi di tipo DDL questa sarebbe l’unica strada percorribile poiché la politica del framework di persistenza è quella di non permette in alcun modo la modifica della struttura del database. Per ottenere il collegamento al datasource si userà l’annotazione @Resource che richiede al server l’injection nel Session Bean della risorsa specificata.

Esempio:

@Stateless 
public class DatabaseServiceBean implements DatabaseService { 
  @Resource(mappedName=”java:/siper/datasource”) 
  DataSource dataSource; 
  public void execute(String sql) throws Exception { 
    Connection con = null; 
    Statement st = null; 
    try { 
      con = dataSource.getConnection(); 
      st = con.createStatement(); 
      st.execute(sql); 
    } finally { 
      try {st.close();} catch (Throwable t) {} 
      try {con.close();} catch (Throwable t) {} 
    } 
  } 
} 

Conclusioni

In questa guida “minimale” abbiamo coperto giusto una piccola frazione delle possibilità offerte dal java persistence framework. Ci sono annotazioni per controllare le transazioni, annotazioni per definire chiavi complesse, annotazioni per definire sequenze come pure annotazioni per definire relazioni tra entità. Resta il fatto che le annotazioni sono intuitive, facili da implementare e ben documentate. Vi auguro quindi un buon approfondimento.

Risorse


Questa guida è stata prodotta da: Aurelio D’Amico il 9 Luglio 2007