G.Morreale
Introduzione:
Lo scopo di questo articolo è quello di creare un applicazione enterprise in grado di sfruttare le seguenti tecnologie
- Entity Bean
- Session Bean
- Servlet
- JSP
Java EE è una piattaforma molto vasta e riuscire a spiegare solo piccole parti di esse richiederebbe molto di più che un semplice articolo.
Nonostante ciò il tentativo è quello di dare un input al programmatore per stimolarne l'interesse e indurlo ad approfondire le singole tecnologie chiamate in ballo in questo esempio.
Al fine di velocizzare l'approccio a Java EE verrà utilizzato Netbeans e di conseguenza tutta la serie di wizard che l'IDE mette a disposizione.
Si cercherà inoltre di inserire nell'articolo dei link per approfondire circa i singoli passi eseguiti.
Cosa sarà in grado di fare l'esempio:
Il progettino che si creerà sarà in grado di estrarre i dati da un tabella di un dbms(mysql in questo caso) e stamparli tramite pagina web a video.
Cosa devo installare per realizzare l'esempio:
nota: Gli ultimi due non strettamente necessari, infatti è possibile effettuare operazioni analoghe con un altro dbms o rinunciare la tool grafico per le query.
- Il Database
- Creare lo schema
- Creare una tabella
- Popolare la tabella
- Creazione Progetto Enterprise
- EJB
- Creare un datasource JDBC
- Creare un persistence unit
- Creare l'entity bean
- Creare un session facade per l'entity bean
- Modulo WEB
- Creare una servlet
- Creare una jsp
Aprire il Mysql query browser loggandosi su localhost, si inserisca un nuovo DB chiamato EsempioDB con la seguente istruzione sql:
CREATE DATABASE `EsempioDB`;
Si crei la tabella:
CREATE TABLE `esempiodb`.`EsempioTable` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`testo` VARCHAR(45) NOT NULL,
PRIMARY KEY (`id`)
)
ENGINE = InnoDB;
E si popoli con le seguenti INSERT:
INSERT INTO EsempioTable VALUES(1,'TESTO 1');
INSERT INTO EsempioTable VALUES(2,'TESTO 2');
INSERT INTO EsempioTable VALUES(3,'TESTO 3');
2. Creazione Progetto "Vuoto"
Adesso si crei un nuovo progetto Enterprise selezionando "New Project" all'interno del file menu di netbeans.
Si scelga un nome, ad esempio, "Esempio" e si lascino inalterate tutte le opzioni richieste fino alla fine del wizard.
Ciò consente a netbeans di creare un progetto EJB, un progetto WEB e un progetto EAR in grado di inglobare EJB e WEB all'interno di unico archivio.
Come è possibile notare nella lista dei progetti saranno presenti i seguenti 3 progetti:
- Esempio
- Esempio-ejb
- Esempio-war
Facendo un breve cenno al design pattern MVC gli ultimi due progetti conterranno rispettivamente il model(ejb) e controller/view(war).
3. EJB
Risorsa JDBC e PERSISTENCE UNIT
Adesso bisogna creare all'interno dell'application server una risorsa jdbc, ovvero una risorsa in grado di comunicare con il dbms e db creato.
nota:
Tale risorsa normalmente viene creata attraverso dei comandi propri dell'application server (es. in glassfish asadmin add-resources sun-resources.xml) o attraverso la console di amministrazione del server stesso, è quindi un operazione non di programmazione ma di amministrazione del container.
Nel caso di glassfish la risorsa jdbc viene creata attraverso un file xml nel quale si indicano una serie di parametri tra i quali username e password del database, numero di connesioni del pool etc.
Esempio di file xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 Resource Definitions //EN" "http://www.sun.com/software/appserver/dtds/sun-resources_1_3.dtd">
<resources>
<jdbc-resource enabled="true" jndi-name="esempioJNDI" object-type="user" pool-name="mysqlPool"/>
<jdbc-connection-pool allow-non-component-callers="false" associate-with-thread="false" connection-creation-retry-attempts="0" connection-creation-retry-interval-in-seconds="10" connection-leak-reclaim="false" connection-leak-timeout-in-seconds="0" connection-validation-method="auto-commit" datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" fail-all-connections="false" idle-timeout-in-seconds="300" is-connection-validation-required="false" is-isolation-level-guaranteed="true" lazy-connection-association="false" lazy-connection-enlistment="false" match-connections="false" max-connection-usage-count="0" max-pool-size="32" max-wait-time-in-millis="60000" name="mysqlPool" non-transactional-connections="false" pool-resize-quantity="2" res-type="javax.sql.DataSource" statement-timeout-in-seconds="-1" steady-pool-size="8" validate-atmost-once-period-in-seconds="0" wrap-jdbc-objects="false">
<property name="serverName" value="localhost"/>
<property name="portNumber" value="3306"/>
<property name="databaseName" value="esempioDB"/>
<property name="User" value="root"/>
<property name="Password" value="123456"/>
<property name="URL" value="jdbc:mysql://localhost:3306/esempioDB"/>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
</jdbc-connection-pool>
</resources>
Netbeans in automatico genera tale file xml e si occupa di comunicare a glassfish il deploy di tale risorsa.
Per far ciò si può passare direttamente a creare il persistence unit(sarebbe il passo successivo) e selezionare sulla combobox DataSource "New Data Source"
Cliccando su New Data Source si abbandona per un attimo la creazione del persistence unit e si passa alla creazione della risorsa per la comunicazione con il db.
Inizialmente verrà chiesto un nome JNDI e un URL jdbc.
Riguardo al primo è possibile scegliere un nome arbitrario,es. esempioJNDI
nota:
Il nome jndi serve a identificare la risorsa all'interno del container, tale percorso però non verrà utilizzato direttamente nel codice in quando usando la dependency injection sarà il container stesso a trovare la risorsa (usando il percorso JNDI)
Configurare il tutto correttamente in modo da ritornare alla finestra "New persistence Unit".
Sulla finestra "New persistence Unit" bisogna operare con le seguenti opzioni:
- Persistence Unit Name: è il nome della configurazione del persistence unit
- Persistence Provider: Indica quale librerie usare per gestire il mapping tra il database e la sua rappresentazione ad oggetti
(di default viene proposto toplink, ma ci sono altre valide alternative, personalmente preferisco hibernate ma per l'esempio si lasci toplink come opzione)
- DataSource: è il nome del Datasource appena creato
- Use Java Transaction API: Indica se far gestire alle JTA le transazioni o meno. (lasciare checked)
- Table generation Strategy:Indica al persistence provider se creare o meno le tabelle nel caso in cui esistano gli entity e non le corrispettive tabelle. (in questo esempio le tabelle sono state create, ma si poteva direttamente creare gli entity che rappresentano le tabelle e lasciare che il persistence provider le generasse in automatico)
In definitiva scelto il nome del persistence unit (ad esempio - Esempio-ejbPU) è possibile proseguire cliccando su Finish lasciando tutte le opzioni inalterate.
Netbeans ha creato in automatico due file
- sun-resources.xml - necessario alla creazione della risorsa jdbc sul server
- persistence.xml - necessaria a indicare una serie di opzioni circa la gestione della persistenza (mapping O/R) etc.
Entity Bean
Cos'è un entity bean? E' un oggetto pojo in grado di rappresentare attraverso la notazione ad oggetti un tabella.
La corrispondenza tra entity bean e tabelle è gestista dal persistence provider (una serie di librerie).
Nel passo precedente è stato scelto TopLink come provider(le sue librerie sono installate di default).
nota:
Ne esistono di due tipi
BMP - entity Bean Managed Persistence
CMP - entity Container Managed Persistence
I primi prevedono che il programmatore di occupi della logica di mapping tra gli entity e il database relazionale, i secondi si appoggiano al persistence provider. Nell'esempio si creerano degli entity CMP.
Tali bean possono essere creati in automatico attraverso l'ennesimo wizard di netbeans.
Tasto destro sul progetto ejb + Entity Classes From Databases..Scegliere il datasource "esempioJNDI" e scegliere le tabelle da sottoporre al wizard (1 sola in questo caso).
Cliccare su next, indicare un package che contenga gli entity, ad esempio entities, cliccare su finish.
nota:
L'opzione "Generated named query annotation ..." serve a generare in automatico delle query all'interno dell'entity, tali query hanno il vantaggio di essere precompilate e quindi leggermente più performanti. In questo esempio comunque non ne viene fatto uso, per cui l'opzione è tralasciabile.
All'interno del package entities è possibile trovare l'entity bean, attraverso le annotations (@Table, @Column etc.) si indica al persistence provider come effettuare il mapping tra i dati del db e quelli nell'entity.
Session Bean
Cos'è un Session bean? E' un componente che gestisce la logica applicativa, nel classico esempio della banca si occuperà di effettuare le operazioni di bonifico, lista movimenti etc.
Ha un particolare ciclo di vita gestito dal container(attraverso pooling) e di solito vengono usati per la gestione del modello richiesta-risposta.
nota:
Esistono due tipi di session bean: Stateless e Stateful, la differenza consiste nel mantenimento delle informazioni tra client e server, solo i secondi infatti sono in grado di conservare tali dati.
Il session bean è composto dalla sua interfaccia e dalla relativa implementazione
nota:
Le interfaccie possono essere di due tipi, Locali e Remote, le prime si usano quando il modulo ejb e il modulo web risiedono all'interno della stessa applicazione enterprise (EAR), ovviamente sono più efficenti. Le seconde vengono usate qualora i moduli ejb e il modulo web(o altri moduli ejb) risiedono su applicazioni diverse o container diversi.
Si ricorre nuovamente al wizard di netbeans per creare in automatico un session bean in grado di implementare ed esporre attraverso interfaccia locale i più comuni metodi (inserimento, edit, cancellazione etc.) di interazione con l'entity appena creato.
Tasto destro sul progetto ejb - Session Bean For Entity Classes - scegliere l'entity - scegliere un package (es. session) -> Finish!!
All'interno del package session è stata creato il session bean e la relativa interfaccia.
L'interfaccia espone i metodi utilizzabili dall'esterno (es. modulo web:servlet)
Si dia uno sguardo all'implementazione del session bean
@Stateless
public class EsempiotableFacade implements EsempiotableFacadeLocal {
@PersistenceContext
private EntityManager em;
public void create(Esempiotable esempiotable)
{
em.persist(esempiotable);
}
public void edit(Esempiotable esempiotable)
{
em.merge(esempiotable);
}
public void remove(Esempiotable esempiotable)
{
em.remove(em.merge(esempiotable));
}
public Esempiotable find(Object id)
{
return em.find(entities.Esempiotable.class, id);
}
public List<Esempiotable> findAll()
{
return em.createQuery("select object(o) from Esempiotable as o").getResultList();
}
}
@Stateless indica banalmente il tipo di bean.
@PersistenceContext
private EntityManager em;
Serve a inizializzare l'EntityManager, ovvero quell'oggetto che si occupa del mapping fra entity e tabelle.
L'EntityManager è una delle classi chiave del persistence provider.
l'annotation @PersistenceContext permette al container di inizializzare la variabile em considerando le informazioni immesse all'interno del file persistence.xml (Si ricordi il persistence unit!!).
L'inizializzazione avviene attraverso il meccanismo della dependency injection (vedi mio articolo precedente).
i metodi "em.find, em.remove, em.merge, em. persist"
sono propri dell'entity manager e servono rispettivamente per le seguenti operazioni di base
- em.find - > ricerca tramite id di un entity (corrisponde ad una SELECT ... WHERE id = :id)
- em.remove -> eliminazione tramite id (corrisponde a DELETE .. WHERE id = :id
- em.merge -> inserimento o update (INSERT o UPDATE) (serve anche a recuperare entity detached...)
- em. persist -> inserimento dell'entity sul db (corrisponde alla INSERT)
Le query da sottoporre all'entitymanager devono essere scritte in EJB-QL, ovvero uno specifico linguaggio in grado di effettuare query sugli entity e non sulle tabelle. EJB-QL contribuisce a garantire la portabilità delle query attraverso dbms diversi (la query scritta infatti prescinde da mysql!).
Per approfondire su ejb-ql clicca qui.
Ovviamente bisogna imparare a scrivere i propri session bean ma l'esempio intende giusto a dare una panoramica..
Modulo WEB
Si è realizzata la logica di business, l'ejb è in grado di estrarre i dati dal db e comunicarli attraverso una struttura dati (List<Esempiotable>)
Adesso è possibile procedere creando una servlet in grado di richiamare il session bean.(Secondo il pattern MVC la servlet fungerà da controller).
Tasto destro sul modulo war -> New Servlet -> Servlet class name = test -> package = servlet -> Finish
Si svuoti il metodo processRequest in modo che sia uguale alla porzione di codice seguente
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
//clicca qui con il tasto dx.
}
Adesso si procede con l'inizializzazione (ancora una volta operata attraverso dependency injection) del componente ejb da richiamare.
Dove c'è il commento premere Tasto destro -> Insert code -> Call Enterprise Bean
In quest'ultimo caso il wizard è servito per inserire una sola riga di codice:
@EJB
private EsempiotableFacadeLocal esempiotableFacade;
Ovvero quella per l'inizializzazione del session bean.
Tale variabile non necessita ulteriori inizializzazioni (grazie all'annotation ci ha già pensato il container!), è quindi pronta all'uso.
All'interno di processRequest è quindi possibile digitare il seguente codice al fine di estrarre il contenuto della tabella.
List<Esempiotable> list = esempiotableFacade.findAll();
//(non dimenticare di fare un fix delle import!)
Bene, già da subito si potrebbe nella servlet stessa stampare il contenuto della List attarverso un PrintWriter o similiari partendo dall'oggetto response.
Ma volendo rispettare il paradigma MVC e predisporre l'esempio per un uso più strutturato si decide di passare i dati a una view (una pagina JSP).
Al fine di passare i dati tra la servlet e la pagina jsp si usa la tecnica del forward attraverso un RequestDispatcher.
Il dispatcher può essere inizializzato nel seguente modo:
String arg = "/" + this.getServletName() + ".jsp";
RequestDispatcher dispatcher = this.getServletContext().getRequestDispatcher(arg);
il forwarding avviene attraverso l'istruzione
dispatcher.forward(request, response);
Tale tecnica non genera un redirect verso la pagina jsp, ma serve a indicare al container che il flusso viene passato dalla servlet alla jsp mantenendo inalterato lo stato dell'oggetto request e response.
In tal modo è quindi possibile comunicare i dati contenuti nella "list" alla jsp settandoli all'interno dell'oggetto request operando nel seguente modo:
request.setAttribute("lista", list);
quindi in definitiva il codice di processRequest sarà il seguente:
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<Esempiotable> list = esempiotableFacade.findAll();
request.setAttribute("lista", list);
String arg = "/" + this.getServletName() + ".jsp";
RequestDispatcher dispatcher = this.getServletContext().getRequestDispatcher(arg);
dispatcher.forward(request, response);
}
Adesso si crei una jsp con lo stesso nome della servlet (test.jsp) (Secondo MVC stiamo realizzando una view)
Il codice della jsp è il seguente:
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<%@page import="entities.Esempiotable"%>
<%@page import="java.util.List"%>
<%
List<Esempiotable> list = (List<Esempiotable>)request.getAttribute("lista");
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<h1>Contenuto Tabella EsempioTable</h1>
<% if (list != null)
{ for (Esempiotable e: list)
{ out.println(e.getId() + " " + e.getTesto() + "<br/>"); }
}
%>
</body>
</html>
Cliccando su Run sull'Enterprise Application Esempio e digitando sul browser
http://localhost/Esempio-war/test
L'output ottenuto sarà simile alla seguente immagine:
Conclusione:
Questo articolo è solo un introduzione al mondo java EE, di cose da spiegare ed esplorare ce no sono veramente tante.. Questo è solo l'inizio!