1
Università degli Studi dell’Aquila
Master in Web Technology
TECNICHE DI PERSISTENZA
A.A. 2013/14
Docente: Francesco Pititto
Studente Matricola Email
Silvio D’Orazio 234422 silvio.dorazio@gmail.com
Alessandra Ponis 234457 alessadra.ponis@gmail.com
2
Indice
1. Strategy ................................................................................................................................. 3
Panoramica dell’applicazione......................................................................................... 3
2. Scope ..................................................................................................................................... 3
2.1 Presentazione degli attori ..................................................................................... 3
2.2 Requisiti................................................................................................................. 4
2.2.1 Requisiti funzionali..................................................................................... 4
2.2.2 Requisiti non funzionali.............................................................................. 5
2.3 Use case diagram .................................................................................................. 6
3. La persistenza dati................................................................................................................. 7
3.1 Class diagram........................................................................................................ 7
3.2 Il framework di persistenza .................................................................................. 9
3.2.1 I mapping delle classi ................................................................................. 9
4. L’architettura del sistema.................................................................................................... 17
3
1. Strategy
Panoramica dell’applicazione
Fare la spesa è un’attività che coinvolge, volente o dolente, tutte le famiglie. Spesso non basta
compiere quest’attività una volta a settimana, bensì, è abitudine andarci spesso. Essendo un
mercato molto importante, soprattutto in una realtà come quella italiana dove la cultura del
buon cibo è sentita, diversi studiosi di marketing hanno elaborato e tutt’ora elaborano piani
strategici affinché l’utente sia invogliato e agevolato durante le fasi della spesa come la ricerca,
l’acquisto e la fidelizzazione. La nostra idea nasce dalla consapevolezza che le nuove tecnologie
sono sempre più rivolte all’utente e sempre più sono presenti ed utilizzate nel quotidiano di
tanti italiani, infatti il WEB con l’e-commerce ha aperto un mercato nuovo pieno di
potenzialità, dove piccole realtà che vi investono risultano vincenti. FastMarket rispecchia la
nostra visione futura del fare la spesa nel supermercato di fiducia della zona di residenza. È più
di un classico e-commerce, in quanto ogni funzionalità è studiata appositamente per l’utente.
Grazie all’ausilio di questa applicazione si potrà comodamente fare la spesa nei ritagli di tempo
attraverso un pc o un tablet, facendosi recapitare il tutto comodamente a casa o ritirandola in
un momento successivo. Ma soprattutto si potrà godere di diversi benefici come una lista della
spesa che non si perderà come i foglietti volanti, un ricettario e la possibilità di acquistare un
pasto o un panino per poi andarlo a prelevare nel punto vendita senza la fila dell’ora di punta.
2. Scope
2.1 Presentazione degli attori
Gli attori che interagiscono attivamente con il sistema sono:
 User;
 Registered user;
 Employee: un attore astratto che rappresenta concettualmente qualsiasi dipendente
del supermercato, creato per rappresentare le funzionalità a cui tutti i dipendenti
possono accedere.
I dipendenti del supermercato, che possono usufruire totalmente o parzialmente delle
funzionalità di backoffice, sono stati divisi per ruolo:
a. Site admin: l’utente amministratore del sito è in grado di accedere a tutte le
funzionalità del backoffice, ovvero le funzionalità a cui possono accedere
anche gli altri dipendenti del supermercato più la gestione dei dipendenti del
supermercato e degli utenti registrati al sito;
b. Sales manager: dipendente incaricato della gestione degli ordini (ovvero sia le
consegne a domicilio che le prenotazioni), della gestione degli articoli e delle e-
mail per assistenza tecnica e servizio clienti.
4
c. Web-marketing manager: dipendente incaricato di gestire le newsletter, le e-
mail promozionali, le informazioni del sito e la sezione ricette.
2.2 Requisiti
2.2.1 Requisiti funzionali
Di seguito presentiamo l’elenco delle funzionalità implementate nell’applicazione suddivise per
attore:
Gli utenti del sito:
User:
 Il sistema consentirà all’utente di effettuare la registrazione, inserendo tutte le
informazioni personali, creando quindi un proprio profilo.
 Il sistema permetterà la ricerca degli articoli del supermarket:
- per categoria (latte, detersivi o quant’altro);
- per tipologia d’intolleranza (celiachia, nefropatia o quant’altro);
- per marca (Barilla, Dixan o quant’altro);
 Il sistema permetterà di visualizzare per ogni articolo le rispettive
caratteristiche e descrizione.
Registered user:
 All’utente registrato, dopo aver effettuato l’autenticazione, sarà possibile
modificare il proprio profilo dal sistema.
Gli utenti amministratori del backoffice (i dipendenti):
Sales manager:
 L’utente con il ruolo di “Responsabile vendite” gestirà l’inserimento, la
cancellazione e la modifica degli articoli del supermercato.
Web-marketing manager:
 L’utente con il ruolo “Responsabile vendite” sarà addetto alla gestione delle
informazioni presenti all’interno del sito.
Site admin:
Oltre a fare tutto ciò che fanno gli altri utenti amministratori del backoffice:
5
 L’utente con il ruolo di “Amministratore del sito” sarà incaricato di gestire i
dipendenti del negozio (sarà l’unico in grado di aggiungere o eliminare i profili dei
dipendenti).
 Tale dipendente sarà anche incaricato di gestire gli utenti registrati e di avere
accesso ai loro dati in caso di problemi con i pagamenti o le prenotazioni.
Inoltre:
 Ciascun dipendente sarà in grado di autenticarsi e modificare il proprio profilo
dal sistema;
2.2.2. Requisiti non funzionali
Usabilità:
 Il funzionamento del sistema risulterà semplice da imparare affinché i nuovi
utenti lo possano utilizzare immediatamente e facile da ricordare per quelli abituali. Gli
utenti non avranno né voglia né tempo di aspettare lunghe elaborazioni, per questo
risulterà efficiente e facilmente navigabile tramite qualsiasi dispositivo.
Affidabilità:
 il servizio di pagamento on-line verrà delegato ad un sistema esterno sicuro ed
affidabile (Paypal) dal quale dipenderà il corretto funzionamento del servizio stesso;
 Per gestire eventuali malfunzionamenti non pervenuti all’amministratore o
particolari condizioni di stress, il sistema prevedrà una funzionalità di “gestione delle e-
mail per assistenza tecnica” affidata ad un addetto “Responsabile vendite” che
permetterà al medesimo di accogliere eventuali segnalazioni di errore da parte degli
utenti.
Performance:
 L’architettura di sistema permetterà a molteplici utenti di usufruire
contemporaneamente dei servizi offerti e sarà in grado di processare varie transazioni
nello stesso momento e di restituire l’output in tempi brevi.
Supportabilità (Manutenibilità):
 Il catalogo online verrà costantemente aggiornato da un responsabile vendite
per rispecchiare le disponibilità del negozio.
 Il sistema verrà costantemente manutenuto da un amministratore che farà
periodicamente dei test per assicurarne il corretto funzionamento su qualsiasi
tecnologia e piattaforma software di ultima generazione.
Scalabilità:
 Il sistema è stato progettato per essere facilmente estendibile a livello di
funzionalità e di componenti software.
Vincoli
I permessi di accesso ai dati degli utenti saranno forniti soltanto all’amministratore del
sistema.
6
2.2 Use case diagram
Lo use case diagram di seguito riporta graficamente i requisiti funzionali del sistema
precedentemente descritti.
7
3. La persistenza dati
3.1 Class diagram
Il class diagram di FastMarket definisce il modello di dominio dell’applicazione ed è composto
in totale da 18 classi, ciascuna delle quali è relazionata con altre classi in base ad un particolare
tipo di associazione.
In ordine alfabetico, il sistema è composto dalle seguenti classi:
 Address: rappresenta l’indirizzo che può essere associato al profilo di un particolare
utente ed è di fondamentale importanza quando un utente registrato si trova in
fase di pagamento. Ad ogni utente possono essere associati più indirizzi.
 Booking: classe che estende da Order creata per rappresentare il concetto di
prenotazione della spesa o del pasto pronto che un utente registrato può effettuare
tramite il sito per poi ritirare i prodotti nel punto vendita. Ogni utente registrato
può effettuare più prenotazioni.
 Brand: ad ogni prodotto, alimentare e non, viene associata una marca particolare
che è rappresentata all’interno dell’applicazione tramite questa classe; ogni marca
può essere associata a più prodotti. Tale classe viene utilizzata all’interno della
pagina “Catalogo prodotti”, accessibile dal front-end, come filtro di ricerca
avanzata.
 Cart: classe che rappresenta il carrello persistente (dunque non in sessione, dato
che il carrello in sessione viene allocato solo per gli utenti non registrati) che viene
associato ad un utente registrato. Ogni utente può gestire un carrello al massimo.
 CartLine: classe che rappresenta il prodotto inserito nel carrello. Ad ogni carrello
possono essere associate più istanze di CartLine.
 Category: ad ogni prodotto, oltre alla marca, viene associata una categoria che
rispecchia le stesse tipologie di prodotti presenti all’interno degli scaffali del
supermercato ed è rappresentata da questa classe. Anche Category viene utilizzata
come filtro di ricerca avanzata nella pagina “Catalogo prodotti”.
 Delivery: classe che estende da Order e rappresenta il concetto di consegna a
domicilio; essa viene creata dopo che l’utente ha inserito l’indirizzo dove preferisce
ricevere la spesa per poi scegliere se pagare on-line o presso la sede del
supermercato. Ogni utente può richiedere al sistema di effettuare più consegne.
 District: rappresenta il comune di residenza che l’utente deve selezionare quando
inserisce un nuovo indirizzo nel sistema. Tale classe è molto importante per il
servizio di consegna a domicilio poiché tramite la stessa il sistema è in grado di
determinare se il camioncino delle consegne del supermercato effettua il servizio
anche verso il comune selezionato o meno.
 Grocery: classe che rappresenta il prodotto alimentare. Grocery estende da Product
e ne aggiunge degli attributi propri.
 IntoleranceCategory: dato che l’applicazione web, a differenza degli altri siti e-
commerce presenti nel mercato, offre l’opportunità di acquistare dei prodotti per
intolleranti, tale classe rappresenta la categoria di intolleranza da eventualmente
8
associare ad alcuni prodotti alimentari presenti nel sistema.
Anche IntoleranceCategory può essere selezionata dall’utente per effettuare una
ricerca avanzata nella pagina “Catalogo prodotti”.
 NonGrocery: classe che rappresenta il prodotto non alimentare. Allo stesso modo di
Grocery, anche questa classe estende da Product e presenta degli attributi propri.
 Order: rappresenta il concetto di ordine che può essere effettuato al sistema
dall’utente in fase di acquisto. E’ una classe padre dalla quale estendono Booking e
Delivery e serve per raggruppare gli attributi che hanno in comune i due concetti di
prenotazione in negozio e consegna a domicilio.
 Product: classe padre che rappresenta il generico concetto di prodotto messo in
vendita dal nostro e-commerce. Da tale classe estendono Grocery e NonGrocery ed
è stata creata per raggruppare gli attributi che tali classi hanno in comune.
 Province: classe che rappresenta la provincia che deve essere necessariamente
associata al comune, che viene poi selezionato quando viene inserito un nuovo
indirizzo all’interno del sistema.
 Region: classe che rappresenta la regione che deve essere necessariamente
associata alla provincia che viene a sua volta associata ad un comune particolare.
 Role: classe che rappresenta il ruolo da attribuire obbligatoriamente all’utente una
volta che lo stesso viene inserito all’interno del sistema.
Ad ogni utente possono essere associati più ruoli e ciò è stato fatto pensando agli
amministratori del back-end (i dipendenti) che, a seconda del ruolo a cui viene loro
dato, possono accedere a diverse funzionalità dell’applicazione.
 SystemInformation: classe che rappresenta i dati che vengono inseriti dai
dipendenti incaricati all’interno del sistema. Si tratta di politiche sulla privacy o altre
informazioni sul funzionamento del servizio che gli utenti possono visualizzare
tramite interfaccia e sono dettati dal supermercato di riferimento.
 User: tale classe rappresenta gli utenti del sito che si dividono in due
macrocategorie: utenti registrati e dipendenti. Ad ogni generico utente registrato
possono essere potenzialmente associati uno o più ruoli.
9
3.2 Il framework di persistenza
Abbiamo curato l’aspetto della persistenza dati facendo uso del framework opensource
Hibernate il quale ci ha permesso, attraverso l’uso dei mapping xml object/relational, di
rendere persistenti in modo automatizzato gli oggetti creati nell’applicazione web all’interno di
tabelle relazionali.
3.2.1 I mapping delle classi
Ogni POJO presente nell’applicazione descrive una classe che compone il modello di business.
Ogni classe entity è un firt-class business object che ha un proprio ciclo di vita, che all’interno
del database relazionale ha una sua identità e che esiste indipendentemente da qualsiasi altra
classe entity del sistema.
Per ciascun POJO presente nell’applicazione web è stato generato automaticamente un
mapping hbm xml con all’interno specificati una serie di metadati che Hibernate è in grado di
comprendere a runtime.
Di seguito presenteremo le opzioni che abbiamo scelto di inserire all’interno dei mapping e le
scelte fatte per mappare le classi e le proprietà all’interno rispettivamente delle tabelle e delle
colonne nel DB.
La Entity Address
Abbiamo scelto di creare il mapping di Address inserendo, come per tutte le altre classi del
sistema, le proprietà della classe all’interno del tag <property> con attributi:
 Lazy=”false” perché altrimenti per fare il lazy loading delle proprietà Hibernate
necessiterebbe di una bytecode instrumentation (tuttavia, questa regola non si
applica a tutti i tipi);
 Access=”field” per far sì che Hibernate acceda a runtime al campo con lo stesso
nome.
 Not-null=”true” applicato soltanto ad alcune colonne del DB, per far sì che
Hibernate segnali proprietà a cui vengono illegalmente assegnati valori nulli.
Invece, il <generator> scelto per creare nuove PK all’interno della tabella ADDRESSES, come
per il resto delle Entity, è di tipo “sequence”. Esso consente di creare nuovi identificativi
tramite una o più sequenze, che possono essere di tipo long, short o int. Nel caso della nostra
applicazione, abbiamo deciso di far creare ad Hibernate una sequenza per ogni Entity
assegnando a ciascuna un nome particolare.
<hibernate-mapping>
<class name="it.univaq.mwt.fastmarket.business.model.Address" table="ADDRESSES">
<id name="id" type="long">
<column name="ID" />
<generator class="sequence">
<param name="sequence">SEQUENCE_ADDRESSES</param>
10
</generator>
</id>
<property access="field" lazy="false" name="street" type="java.lang.String">
<column name="STREET" not-null="true" />
</property>
<property access="field" lazy="false" name="streetNumber" type="int">
<column name="STREETNUMBER" not-null="true" />
</property>
<property access="field" lazy="false" name="country" type="java.lang.String">
<column name="COUNTRY" not-null="true" />
</property>
<property access="field" lazy="false" name="zipCode" type="int">
<column name="ZIPCODE" not-null="true" />
</property>
<many-to-one name="district"
class="it.univaq.mwt.fastmarket.business.model.District" access="field" fetch="join">
<column name="DISTRICT_ID" not-null="true" />
</many-to-one>
<many-to-one name="user" class="it.univaq.mwt.fastmarket.business.model.User"
access="field" fetch="join">
<column name="USER_ID" not-null="true" />
</many-to-one>
</class>
</hibernate-mapping>
Come è possibile notare, all’interno del mapping sono specificate due associazioni di tipo
<many-to-one> verso le classi District e User. Ciò significa che nella tabella ADDRESSES viene
riservata una colonna per le rispettive FK verso le tabelle DISTRICTS e USERS.
Abbiamo scelto questo tipo di associazione perché ci aspettiamo che svariati indirizzi possano
essere associati ad uno stesso comune e che un utente possa avere diversi indirizzi, uno dei
quali può essere selezionato durante la fase di pagamento per indicare al dipendente dove
preferisce ricevere la spesa.
Per quanto riguarda gli attributi dell’associazione, abbiamo inserito:
 Access=”field” per consentire l’accesso diretto al campo per ogni proprietà
specificata.
 Fetch=”join” che disabilita il lazy load e carica oltre all’entità padre l’intera
collezione referenziata.
Le Entity District, Province e SystemInformation
Esistono vari casi in cui una Entity del sistema presenta un mapping analogo a quello descritto
in precedenza, con una o più associazioni di tipo <many-to-one>, ciascuna scelta in base ad un
criterio ben preciso:
 La Entity District con la Entity Province di modo da poter associare diversi comuni
ad ogni provincia;
 La Entity Province con la Entity Region di modo da poter associare diverse province
ad una sola regione;
 La Entity SystemInformation con la Entity User di modo da poter associare diversi
dati riguardanti il sistema ad un solo utente autore;
11
Le Entity Cart e CarLine
Stabilire il tipo di associazione tra la classe Cart e CartLine è stato tutt’altro che semplice.
Abbiamo scelto di optare, infine, per un’associazione <many-to-one> all’interno del mapping di
CartLine in cui la FK all’interno della tabella di CartLine fa riferimento alla PK della tabella di
Cart.
<hibernate-mapping>
<class name="it.univaq.mwt.fastmarket.business.model.Cart" table="CARTS">
<id name="id" type="long">
<column name="ID" />
<generator class="sequence">
<param name="sequence">SEQUENCE_CARTS</param>
</generator>
</id>
<property access="field" lazy="false" name="name" type="java.lang.String">
<column name="NAME" not-null="true" />
</property>
<property access="field" lazy="false" name="totalPrice" type="float">
<column name="TOTALPRICE" />
</property>
<many-to-one name="user" class="it.univaq.mwt.fastmarket.business.model.User"
access="field" fetch="join">
<column name="USER_ID" unique="true" not-null="true" />
</many-to-one>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="it.univaq.mwt.fastmarket.business.model.CartLine" table="CARTLINES">
<id name="id" type="long">
<column name="ID" />
<generator class="sequence">
<param name="sequence">SEQUENCE_CARTLINES</param>
</generator>
</id>
<property access="field" lazy="false" name="quantity" type="int">
<column name="QUANTITY" not-null="true" />
</property>
<many-to-one name="cart" class="it.univaq.mwt.fastmarket.business.model.Cart"
access="field" fetch="join">
<column name="CART_ID" not-null="true" />
</many-to-one>
<many-to-one name="product"
class="it.univaq.mwt.fastmarket.business.model.Product" access="field" fetch="join">
<column name="PRODUCT_ID" not-null="true" />
</many-to-one>
</class>
</hibernate-mapping>
La scelta del mapping <many-to-one> tra Cart e User serve in realtà ad esprimere
un’associazione one-to-one unidirezionale per la quale all’interno della tabella CARTS viene
inserita la FK che referenzia la PK della tabella associata USERS.
Alla colonna dentro a CARTS dove viene inserita la FK verso USERS è stato necessario, tuttavia,
specificare la clausola unique=”true” per far sì che Hibernate legga realmente la relazione
come one-to-one.
Invece, la scelta del mapping <many-to-one> tra CarLine e Cart è dettata dal fatto che,
inserendo la FK all’interno della tabella CARTLINES, riusciamo ad assicurarci che una o più
istanze di CartLine siano associate ad un solo carrello; per rafforzare tale unicità, abbiamo
anche gestito il lancio dell’eccezione di violazione del vincolo di chiave univoca all’interno
dell’applicazione nel caso in cui l’utente cerchi di associare una stessa CartLine a più carrelli.
12
In realtà, essendo un’applicazione e-commerce che consente ad ogni utente di avere un solo
carrello, abbiamo pensato che l’utente registrato eliminerebbe dal carrello soltanto una
cartLine per volta perciò non si produrrebbe mai l’eventualità in cui un utente tenta di
eliminare direttamente il carrello riempito dalle cartLines (eventualità che viene, ad ogni
modo, gestita nel backend dell’app nel caso in cui un dipendente del supermercato cerchi di
farlo).
Infine, all’interno di CartLine abbiamo dichiarato un'altra associazione <many-to-one> per
creare un riferimento verso la Entity Product, dato che svariate righe del carrello possono
contenere uno stesso prodotto.
Le Entity Order, Booking e Delivery
Nel caso di Order, Booking e Delivery le Entity sono legate tramite una generalizzazione, per la
quale Booking e Delivery estendono da Order. Tra le varie strategie a disposizione per mappare
l’ereditarietà, abbiamo scelto quello che W. Keller ha definito “One Class One Table”, ovvero
abbiamo mappato sia le proprietà della classe astratta che le proprietà delle classi concrete
creando così 3 tabelle diverse; ciò significa che ciascuna tabella contiene gli attributi della
rispettiva classe. In questo caso, dato che Delivery e Booking ereditano anche la chiave
primaria, il join tra le tabelle viene attuato tramite la colonna della PK all’interno della tabella
padre.
Abbiamo deciso di optare per questo mapping poiché, tra le varie opzioni disponibili, è
ottimale a livello di consumo di spazio nel DB e questo aspetto è per noi fondamentale dato
che nel nostro caso la classe Booking presenta svariati attributi.
A livello di mapping xml è stato, dunque, generato un unico mapping per la classe padre Order
con all’interno specificati i tag <joined-subclass> con internamente presente l’attributo
extends.
<hibernate-mapping>
<class abstract="true" name="it.univaq.mwt.fastmarket.business.model.Order" table="ORDERS">
<id name="id" type="long">
<column name="ID" />
<generator class="sequence">
<param name="sequence">SEQUENCE_ORDERS</param>
</generator>
</id>
<property access="field" lazy="false" name="creationDate" type="date">
<column name="CREATIONDATE" not-null="true" />
</property>
<property access="field" lazy="false" name="totalPrice" type="float">
<column name="TOTALPRICE" not-null="true" />
</property>
<many-to-one name="cart" class="it.univaq.mwt.fastmarket.business.model.Cart"
access="field" fetch="join">
<column name="CART_ID" not-null="true" />
</many-to-one>
<joined-subclass name="it.univaq.mwt.fastmarket.business.model.Booking"
extends="it.univaq.mwt.fastmarket.business.model.Order"
table="BOOKINGS">
<key>
<column name="ID" not-null="true" />
</key>
<property access="field" lazy="false" name="expirationDate" type="date">
<column name="EXPIRATIONDATE" not-null="true" />
</property>
</joined-subclass>
<joined-subclass name="it.univaq.mwt.fastmarket.business.model.Delivery"
13
extends="it.univaq.mwt.fastmarket.business.model.Order"
table="DELIVERIES">
<key>
<column name="ID" not-null="true" />
</key>
<property access="field" lazy="false" name="dispatchDate" type="date">
<column name="DISPATCHDATE" not-null="true" />
</property>
<property access="field" lazy="false" name="deliveryCosts" type="float">
<column name="DELIVERYCOSTS" not-null="true" />
</property>
<property access="field" lazy="false" name="deliveryType"
type="java.lang.String">
<column name="DELIVERYTYPE" />
</property>
</joined-subclass>
</class>
</hibernate-mapping>
Le Entity Product, Grocery e NonGrocery
Un altro caso di generalizzazione mette in relazione le Entity Product, Grocery e NonGrocery. Il
mapping è analogo a quello appena descritto tranne per il fatto che la classe padre ha anche
una FK verso Brand e una verso Category e la classe figlia Grocery ha una FK verso
IntoleranceCategory.
Anche in questo caso è stato creato un unico mapping per la Entity Product con all’interno le
entità figlie Grocery e NonGrocery, che vengono dichiate tramite l’attributo <joined-
subclass>.
<hibernate-mapping>
<class name="it.univaq.mwt.fastmarket.business.model.Product" table="PRODUCTS">
<id name="id" type="long">
<column name="ID" />
<generator class="sequence">
<param name="sequence">SEQUENCE_PRODUCTS</param>
</generator>
</id>
<property access="field" lazy="false" name="name" type="java.lang.String">
<column name="NAME" not-null="true" unique="true" />
</property>
<property access="field" lazy="false" name="description"
type="java.lang.String">
<column name="DESCRIPTION" />
</property>
<property access="field" lazy="false" name="price" type="float">
<column name="PRICE" not-null="true" />
</property>
<property access="field" lazy="false" name="stock" type="int">
<column name="STOCK" not-null="true" />
</property>
<many-to-one name="brand" class="it.univaq.mwt.fastmarket.business.model.Brand"
access="field" fetch="join">
<column name="BRAND_ID" not-null="true" />
</many-to-one>
<many-to-one name="category"
class="it.univaq.mwt.fastmarket.business.model.Category" access="field" fetch="join">
<column name="CATEGORY_ID" not-null="true" />
</many-to-one>
<property access="field" lazy="false" name="path" type="java.lang.String">
<column name="PATH" />
</property>
<joined-subclass name="it.univaq.mwt.fastmarket.business.model.Grocery"
extends="it.univaq.mwt.fastmarket.business.model.Product" table="GROCERIES">
<key>
<column name="ID" not-null="true" />
</key>
14
<property access="field" lazy="false" name="expirationDate" type="date">
<column name="EXPIRATIONDATE" not-null="true" />
</property>
<property access="field" lazy="false" name="pricePerKg" type="float">
<column name="PRICEPERKG" />
</property>
<property access="field" lazy="false" name="pricePerLt" type="float">
<column name="PRICEPERLT" />
</property>
<many-to-one name="intoleranceCategory"
class="it.univaq.mwt.fastmarket.business.model.IntoleranceCategory" fetch="join">
<column name="INTOLERANCECATEGORY_ID" />
</many-to-one>
</joined-subclass>
<joined-subclass name="it.univaq.mwt.fastmarket.business.model.NonGrocery"
extends="it.univaq.mwt.fastmarket.business.model.Product" table="NONGROCERIES">
<key>
<column name="ID" not-null="true" />
</key>
<property access="field" lazy="false" name="scopeOfUse"
type="java.lang.String">
<column name="SCOPEOFUSE" />
</property>
</joined-subclass>
</class>
</hibernate-mapping>
Le Entity User e Role
Nel caso di User e Role, abbiamo deciso di definire una relazione bidirezionale che permette di
navigare verso entrambi i lati dell’associazione.
In questo modo, ci è possibile ottenere con facilità sia tutti gli utenti a cui sono stati associati
uno o più ruoli che tutti i ruoli che sono stati associati ad uno o più utenti.
A livello di mapping, le due classi entity vengono messe in relazione dichiarando una collezione
di oggetti sia all’interno del mapping di User che in quello di Role.
<hibernate-mapping>
<class name="it.univaq.mwt.fastmarket.business.model.User" table="USERS">
<id name="id" type="long">
<column name="ID" />
<generator class="sequence">
<param name="sequence">SEQUENCE_USERS</param>
</generator>
</id>
<property access="field" lazy="false" name="username" type="java.lang.String">
<column name="USERNAME" not-null="true" unique="true" />
</property>
<property access="field" lazy="false" name="password" type="java.lang.String">
<column name="PASSWORD" not-null="true" />
</property>
<property access="field" lazy="false" name="firstName" type="java.lang.String">
<column name="FIRSTNAME" not-null="true" />
</property>
<property access="field" lazy="false" name="middleName" type="java.lang.String">
<column name="MIDDLENAME" />
</property>
<property access="field" lazy="false" name="lastName" type="java.lang.String">
<column name="LASTNAME" not-null="true" />
</property>
<property access="field" lazy="false" name="gender" type="char">
<column name="GENDER" />
</property>
<property access="field" lazy="false" name="mobilePhone"
type="java.lang.String">
<column name="MOBILEPHONE" />
</property>
<property access="field" lazy="false" name="telephone" type="java.lang.String">
<column name="TELEPHONE" />
</property>
15
<property access="field" lazy="false" name="email" type="java.lang.String">
<column name="EMAIL" not-null="true" />
</property>
<set name="roles" table="USERS_ROLES" inverse="false" access="field"
lazy="true">
<key>
<column name="USER_ID" not-null="true" />
</key>
<many-to-many class="it.univaq.mwt.fastmarket.business.model.Role"
column="ROLE_ID" />
</set>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="it.univaq.mwt.fastmarket.business.model.Role" table="ROLES">
<id name="id" type="long">
<column name="ID" />
<generator class="sequence">
<param name="sequence">SEQUENCE_ROLES</param>
</generator>
</id>
<property access="field" lazy="false" name="name" type="java.lang.String">
<column name="NAME" not-null="true" />
</property>
<property access="field" lazy="false" name="description" type="text">
<column name="DESCRIPTION" />
</property>
<set name="users" table="USERS_ROLES" cascade="save-update" inverse="true"
access="field" lazy="true">
<key>
<column name="ROLE_ID" />
</key>
<many-to-many class="it.univaq.mwt.fastmarket.business.model.User" column="USER_ID"
/>
</set>
</class>
</hibernate-mapping>
Analizzando il mapping di User, notiamo che alla property username è specificato l’attributo
unique=”true”. Esso è stato applicato anche ad altre proprietà presenti in altri mapping (es.
alla proprietà name di Brand) e permette di generare istruzioni DDL di vincolo di unicità alla
colonna corrispondente nel DB.
Per quanto riguarda, invece, le collezioni di oggetti presenti all’interno di entrambi i mapping
User e Role:
 abbiamo scelto di dichiarare un set di oggetti poiché corrisponde all’interfaccia
java.util.Set e ci siamo resi conto che fa al caso nostro poiché, oltre a preservare
l’ordine degli oggetti, non ammette duplicati;
 abbiamo inserito l’attributo cascade=”save-update” cosicché Hibernate navighi
l’associazione ogniqualvolta un oggetto viene salvato o modificato e renda
persistenti le istanze transienti o le modifiche apportate alle istanze fuori dal
contesto transazionale;
 abbiamo dichiarato l’attributo inverse=”true” all’interno del set users dentro al
mapping di Role per informare Hibernate quale lato dell’associazione non deve
sincronizzare con il DB.
In questo caso viene istruito Hibernate di ignorare qualsiasi cambiamento
apportato alla collezione users e, al contrario, di sincronizzare con il database
16
soltanto la collezione roles tramite la tabella associativa. Facendo ciò, evitiamo di
eseguire due volte lo stesso statement SQL sulla stessa FK.
 Abbiamo optato per l’inserimento di lazy=”true” che è in realtà la strategia di
default, di modo da non richiedere ad Hibernate di caricare dal database oltre
all’istanza della Entity anche la collezione di oggetti associati.
17
4. L’architettura del sistema
Per realizzare l’applicazione FastMarket, abbiamo optato per un’architettura J2EE nella quale i
vari componenti architetturali si trovano sulla stessa JVM ed interagiscono tra loro a livello
“locale” al fine di far funzionare l’intero sistema.
Più precisamente, abbiamo un modello architetturale nel quale vi è da un lato un client web
component locale che gestisce la parte di presentazione, da un altro i componenti lato server
che implementano l’intera logica di business e da un altro ancora un EIS (Enterprise
Information System) che fornisce i dati, che in questo caso è un DB Oracle.
Il web component che si occupa sia della lookup JNDI verso l’interfaccia locale degli EJB che
della gestione della parte web (ovvero del rendering dell’interfaccia), la logica di business
(ovvero gli EJB) e il modello di dominio vengono impacchettati all’interno di un file con
estensione .ear ed assemblati all’interno dell’application server Glassfish.
Una volta che l’.ear viene deployato ed eseguito su Glassfish il ContextLoaderListener di Spring
legge il file root-context e, in base ai parametri specificati internamente, fa una lookup JNDI
verso gli EJB che si trovano dentro all’EJB container dell’application server. La Dispatcher
Servlet si occupa invece di costruire i componenti controller per gestire le richieste http e, per
fare ciò, inietta all’interno degli stessi ogni interfaccia remota che trova annotata con
@Autowired.
Per essere più precisi, il client invoca uno o più metodi dell’interfaccia locale annotata con
@Local che a sua volta richiama i relativi metodi dichiarati nel session bean (EJB) stateless –
ovvero che non tiene memoria della transazione svolta – annotato appunto con @Stateless.
All’interno di ciascun bean viene dichiarata la relativa interfaccia locale tramite l’annotazione
@Local(NomeInterfaccia.class) mentre ogni metodo viene annotato tramite
@TransactionAttribute(TransactionAttributeType.REQUIRED) per indicare che se al client che
invoca un metodo del bean è associato un determinato contesto transazionale, allora il
container richiamerà il metodo del bean all’interno di tale contesto. Inoltre, l’annotazione
@TransactionManagement(TransactionManagementType.CONTAINER) posta sopra alla classe
indica che tutti i metodi vengono gestiti dal container.
La gestione della persistenza dati viene, invece, delegata al framework Hibernate tramite
l’utilizzo delle sessioni e dei mapping xml object/relational contenuti all’interno del modello di
dominio mentre il servizio di connessione al DB Oracle è fornito dall’application server tramite
il connettore JDBC.
Alla fine di ogni processo di elaborazione delle richieste http, l’applicazione web eseguita su
Glassfish gestisce la parte di vista da renderizzare all’utente tramite la tecnologia per
implementare le view nota come JavaServer Pages. In realtà, però, la costruzione della vista
viene delegata al framework di templating Apache tiles il quale assembla diversi frammenti
(jsp) per costruire l’intera pagina web.
18
Di seguito presentiamo graficamente l’architettura del sistema finora descritto:

[MWT] TP

  • 1.
    1 Università degli Studidell’Aquila Master in Web Technology TECNICHE DI PERSISTENZA A.A. 2013/14 Docente: Francesco Pititto Studente Matricola Email Silvio D’Orazio 234422 silvio.dorazio@gmail.com Alessandra Ponis 234457 alessadra.ponis@gmail.com
  • 2.
    2 Indice 1. Strategy .................................................................................................................................3 Panoramica dell’applicazione......................................................................................... 3 2. Scope ..................................................................................................................................... 3 2.1 Presentazione degli attori ..................................................................................... 3 2.2 Requisiti................................................................................................................. 4 2.2.1 Requisiti funzionali..................................................................................... 4 2.2.2 Requisiti non funzionali.............................................................................. 5 2.3 Use case diagram .................................................................................................. 6 3. La persistenza dati................................................................................................................. 7 3.1 Class diagram........................................................................................................ 7 3.2 Il framework di persistenza .................................................................................. 9 3.2.1 I mapping delle classi ................................................................................. 9 4. L’architettura del sistema.................................................................................................... 17
  • 3.
    3 1. Strategy Panoramica dell’applicazione Farela spesa è un’attività che coinvolge, volente o dolente, tutte le famiglie. Spesso non basta compiere quest’attività una volta a settimana, bensì, è abitudine andarci spesso. Essendo un mercato molto importante, soprattutto in una realtà come quella italiana dove la cultura del buon cibo è sentita, diversi studiosi di marketing hanno elaborato e tutt’ora elaborano piani strategici affinché l’utente sia invogliato e agevolato durante le fasi della spesa come la ricerca, l’acquisto e la fidelizzazione. La nostra idea nasce dalla consapevolezza che le nuove tecnologie sono sempre più rivolte all’utente e sempre più sono presenti ed utilizzate nel quotidiano di tanti italiani, infatti il WEB con l’e-commerce ha aperto un mercato nuovo pieno di potenzialità, dove piccole realtà che vi investono risultano vincenti. FastMarket rispecchia la nostra visione futura del fare la spesa nel supermercato di fiducia della zona di residenza. È più di un classico e-commerce, in quanto ogni funzionalità è studiata appositamente per l’utente. Grazie all’ausilio di questa applicazione si potrà comodamente fare la spesa nei ritagli di tempo attraverso un pc o un tablet, facendosi recapitare il tutto comodamente a casa o ritirandola in un momento successivo. Ma soprattutto si potrà godere di diversi benefici come una lista della spesa che non si perderà come i foglietti volanti, un ricettario e la possibilità di acquistare un pasto o un panino per poi andarlo a prelevare nel punto vendita senza la fila dell’ora di punta. 2. Scope 2.1 Presentazione degli attori Gli attori che interagiscono attivamente con il sistema sono:  User;  Registered user;  Employee: un attore astratto che rappresenta concettualmente qualsiasi dipendente del supermercato, creato per rappresentare le funzionalità a cui tutti i dipendenti possono accedere. I dipendenti del supermercato, che possono usufruire totalmente o parzialmente delle funzionalità di backoffice, sono stati divisi per ruolo: a. Site admin: l’utente amministratore del sito è in grado di accedere a tutte le funzionalità del backoffice, ovvero le funzionalità a cui possono accedere anche gli altri dipendenti del supermercato più la gestione dei dipendenti del supermercato e degli utenti registrati al sito; b. Sales manager: dipendente incaricato della gestione degli ordini (ovvero sia le consegne a domicilio che le prenotazioni), della gestione degli articoli e delle e- mail per assistenza tecnica e servizio clienti.
  • 4.
    4 c. Web-marketing manager:dipendente incaricato di gestire le newsletter, le e- mail promozionali, le informazioni del sito e la sezione ricette. 2.2 Requisiti 2.2.1 Requisiti funzionali Di seguito presentiamo l’elenco delle funzionalità implementate nell’applicazione suddivise per attore: Gli utenti del sito: User:  Il sistema consentirà all’utente di effettuare la registrazione, inserendo tutte le informazioni personali, creando quindi un proprio profilo.  Il sistema permetterà la ricerca degli articoli del supermarket: - per categoria (latte, detersivi o quant’altro); - per tipologia d’intolleranza (celiachia, nefropatia o quant’altro); - per marca (Barilla, Dixan o quant’altro);  Il sistema permetterà di visualizzare per ogni articolo le rispettive caratteristiche e descrizione. Registered user:  All’utente registrato, dopo aver effettuato l’autenticazione, sarà possibile modificare il proprio profilo dal sistema. Gli utenti amministratori del backoffice (i dipendenti): Sales manager:  L’utente con il ruolo di “Responsabile vendite” gestirà l’inserimento, la cancellazione e la modifica degli articoli del supermercato. Web-marketing manager:  L’utente con il ruolo “Responsabile vendite” sarà addetto alla gestione delle informazioni presenti all’interno del sito. Site admin: Oltre a fare tutto ciò che fanno gli altri utenti amministratori del backoffice:
  • 5.
    5  L’utente conil ruolo di “Amministratore del sito” sarà incaricato di gestire i dipendenti del negozio (sarà l’unico in grado di aggiungere o eliminare i profili dei dipendenti).  Tale dipendente sarà anche incaricato di gestire gli utenti registrati e di avere accesso ai loro dati in caso di problemi con i pagamenti o le prenotazioni. Inoltre:  Ciascun dipendente sarà in grado di autenticarsi e modificare il proprio profilo dal sistema; 2.2.2. Requisiti non funzionali Usabilità:  Il funzionamento del sistema risulterà semplice da imparare affinché i nuovi utenti lo possano utilizzare immediatamente e facile da ricordare per quelli abituali. Gli utenti non avranno né voglia né tempo di aspettare lunghe elaborazioni, per questo risulterà efficiente e facilmente navigabile tramite qualsiasi dispositivo. Affidabilità:  il servizio di pagamento on-line verrà delegato ad un sistema esterno sicuro ed affidabile (Paypal) dal quale dipenderà il corretto funzionamento del servizio stesso;  Per gestire eventuali malfunzionamenti non pervenuti all’amministratore o particolari condizioni di stress, il sistema prevedrà una funzionalità di “gestione delle e- mail per assistenza tecnica” affidata ad un addetto “Responsabile vendite” che permetterà al medesimo di accogliere eventuali segnalazioni di errore da parte degli utenti. Performance:  L’architettura di sistema permetterà a molteplici utenti di usufruire contemporaneamente dei servizi offerti e sarà in grado di processare varie transazioni nello stesso momento e di restituire l’output in tempi brevi. Supportabilità (Manutenibilità):  Il catalogo online verrà costantemente aggiornato da un responsabile vendite per rispecchiare le disponibilità del negozio.  Il sistema verrà costantemente manutenuto da un amministratore che farà periodicamente dei test per assicurarne il corretto funzionamento su qualsiasi tecnologia e piattaforma software di ultima generazione. Scalabilità:  Il sistema è stato progettato per essere facilmente estendibile a livello di funzionalità e di componenti software. Vincoli I permessi di accesso ai dati degli utenti saranno forniti soltanto all’amministratore del sistema.
  • 6.
    6 2.2 Use casediagram Lo use case diagram di seguito riporta graficamente i requisiti funzionali del sistema precedentemente descritti.
  • 7.
    7 3. La persistenzadati 3.1 Class diagram Il class diagram di FastMarket definisce il modello di dominio dell’applicazione ed è composto in totale da 18 classi, ciascuna delle quali è relazionata con altre classi in base ad un particolare tipo di associazione. In ordine alfabetico, il sistema è composto dalle seguenti classi:  Address: rappresenta l’indirizzo che può essere associato al profilo di un particolare utente ed è di fondamentale importanza quando un utente registrato si trova in fase di pagamento. Ad ogni utente possono essere associati più indirizzi.  Booking: classe che estende da Order creata per rappresentare il concetto di prenotazione della spesa o del pasto pronto che un utente registrato può effettuare tramite il sito per poi ritirare i prodotti nel punto vendita. Ogni utente registrato può effettuare più prenotazioni.  Brand: ad ogni prodotto, alimentare e non, viene associata una marca particolare che è rappresentata all’interno dell’applicazione tramite questa classe; ogni marca può essere associata a più prodotti. Tale classe viene utilizzata all’interno della pagina “Catalogo prodotti”, accessibile dal front-end, come filtro di ricerca avanzata.  Cart: classe che rappresenta il carrello persistente (dunque non in sessione, dato che il carrello in sessione viene allocato solo per gli utenti non registrati) che viene associato ad un utente registrato. Ogni utente può gestire un carrello al massimo.  CartLine: classe che rappresenta il prodotto inserito nel carrello. Ad ogni carrello possono essere associate più istanze di CartLine.  Category: ad ogni prodotto, oltre alla marca, viene associata una categoria che rispecchia le stesse tipologie di prodotti presenti all’interno degli scaffali del supermercato ed è rappresentata da questa classe. Anche Category viene utilizzata come filtro di ricerca avanzata nella pagina “Catalogo prodotti”.  Delivery: classe che estende da Order e rappresenta il concetto di consegna a domicilio; essa viene creata dopo che l’utente ha inserito l’indirizzo dove preferisce ricevere la spesa per poi scegliere se pagare on-line o presso la sede del supermercato. Ogni utente può richiedere al sistema di effettuare più consegne.  District: rappresenta il comune di residenza che l’utente deve selezionare quando inserisce un nuovo indirizzo nel sistema. Tale classe è molto importante per il servizio di consegna a domicilio poiché tramite la stessa il sistema è in grado di determinare se il camioncino delle consegne del supermercato effettua il servizio anche verso il comune selezionato o meno.  Grocery: classe che rappresenta il prodotto alimentare. Grocery estende da Product e ne aggiunge degli attributi propri.  IntoleranceCategory: dato che l’applicazione web, a differenza degli altri siti e- commerce presenti nel mercato, offre l’opportunità di acquistare dei prodotti per intolleranti, tale classe rappresenta la categoria di intolleranza da eventualmente
  • 8.
    8 associare ad alcuniprodotti alimentari presenti nel sistema. Anche IntoleranceCategory può essere selezionata dall’utente per effettuare una ricerca avanzata nella pagina “Catalogo prodotti”.  NonGrocery: classe che rappresenta il prodotto non alimentare. Allo stesso modo di Grocery, anche questa classe estende da Product e presenta degli attributi propri.  Order: rappresenta il concetto di ordine che può essere effettuato al sistema dall’utente in fase di acquisto. E’ una classe padre dalla quale estendono Booking e Delivery e serve per raggruppare gli attributi che hanno in comune i due concetti di prenotazione in negozio e consegna a domicilio.  Product: classe padre che rappresenta il generico concetto di prodotto messo in vendita dal nostro e-commerce. Da tale classe estendono Grocery e NonGrocery ed è stata creata per raggruppare gli attributi che tali classi hanno in comune.  Province: classe che rappresenta la provincia che deve essere necessariamente associata al comune, che viene poi selezionato quando viene inserito un nuovo indirizzo all’interno del sistema.  Region: classe che rappresenta la regione che deve essere necessariamente associata alla provincia che viene a sua volta associata ad un comune particolare.  Role: classe che rappresenta il ruolo da attribuire obbligatoriamente all’utente una volta che lo stesso viene inserito all’interno del sistema. Ad ogni utente possono essere associati più ruoli e ciò è stato fatto pensando agli amministratori del back-end (i dipendenti) che, a seconda del ruolo a cui viene loro dato, possono accedere a diverse funzionalità dell’applicazione.  SystemInformation: classe che rappresenta i dati che vengono inseriti dai dipendenti incaricati all’interno del sistema. Si tratta di politiche sulla privacy o altre informazioni sul funzionamento del servizio che gli utenti possono visualizzare tramite interfaccia e sono dettati dal supermercato di riferimento.  User: tale classe rappresenta gli utenti del sito che si dividono in due macrocategorie: utenti registrati e dipendenti. Ad ogni generico utente registrato possono essere potenzialmente associati uno o più ruoli.
  • 9.
    9 3.2 Il frameworkdi persistenza Abbiamo curato l’aspetto della persistenza dati facendo uso del framework opensource Hibernate il quale ci ha permesso, attraverso l’uso dei mapping xml object/relational, di rendere persistenti in modo automatizzato gli oggetti creati nell’applicazione web all’interno di tabelle relazionali. 3.2.1 I mapping delle classi Ogni POJO presente nell’applicazione descrive una classe che compone il modello di business. Ogni classe entity è un firt-class business object che ha un proprio ciclo di vita, che all’interno del database relazionale ha una sua identità e che esiste indipendentemente da qualsiasi altra classe entity del sistema. Per ciascun POJO presente nell’applicazione web è stato generato automaticamente un mapping hbm xml con all’interno specificati una serie di metadati che Hibernate è in grado di comprendere a runtime. Di seguito presenteremo le opzioni che abbiamo scelto di inserire all’interno dei mapping e le scelte fatte per mappare le classi e le proprietà all’interno rispettivamente delle tabelle e delle colonne nel DB. La Entity Address Abbiamo scelto di creare il mapping di Address inserendo, come per tutte le altre classi del sistema, le proprietà della classe all’interno del tag <property> con attributi:  Lazy=”false” perché altrimenti per fare il lazy loading delle proprietà Hibernate necessiterebbe di una bytecode instrumentation (tuttavia, questa regola non si applica a tutti i tipi);  Access=”field” per far sì che Hibernate acceda a runtime al campo con lo stesso nome.  Not-null=”true” applicato soltanto ad alcune colonne del DB, per far sì che Hibernate segnali proprietà a cui vengono illegalmente assegnati valori nulli. Invece, il <generator> scelto per creare nuove PK all’interno della tabella ADDRESSES, come per il resto delle Entity, è di tipo “sequence”. Esso consente di creare nuovi identificativi tramite una o più sequenze, che possono essere di tipo long, short o int. Nel caso della nostra applicazione, abbiamo deciso di far creare ad Hibernate una sequenza per ogni Entity assegnando a ciascuna un nome particolare. <hibernate-mapping> <class name="it.univaq.mwt.fastmarket.business.model.Address" table="ADDRESSES"> <id name="id" type="long"> <column name="ID" /> <generator class="sequence"> <param name="sequence">SEQUENCE_ADDRESSES</param>
  • 10.
    10 </generator> </id> <property access="field" lazy="false"name="street" type="java.lang.String"> <column name="STREET" not-null="true" /> </property> <property access="field" lazy="false" name="streetNumber" type="int"> <column name="STREETNUMBER" not-null="true" /> </property> <property access="field" lazy="false" name="country" type="java.lang.String"> <column name="COUNTRY" not-null="true" /> </property> <property access="field" lazy="false" name="zipCode" type="int"> <column name="ZIPCODE" not-null="true" /> </property> <many-to-one name="district" class="it.univaq.mwt.fastmarket.business.model.District" access="field" fetch="join"> <column name="DISTRICT_ID" not-null="true" /> </many-to-one> <many-to-one name="user" class="it.univaq.mwt.fastmarket.business.model.User" access="field" fetch="join"> <column name="USER_ID" not-null="true" /> </many-to-one> </class> </hibernate-mapping> Come è possibile notare, all’interno del mapping sono specificate due associazioni di tipo <many-to-one> verso le classi District e User. Ciò significa che nella tabella ADDRESSES viene riservata una colonna per le rispettive FK verso le tabelle DISTRICTS e USERS. Abbiamo scelto questo tipo di associazione perché ci aspettiamo che svariati indirizzi possano essere associati ad uno stesso comune e che un utente possa avere diversi indirizzi, uno dei quali può essere selezionato durante la fase di pagamento per indicare al dipendente dove preferisce ricevere la spesa. Per quanto riguarda gli attributi dell’associazione, abbiamo inserito:  Access=”field” per consentire l’accesso diretto al campo per ogni proprietà specificata.  Fetch=”join” che disabilita il lazy load e carica oltre all’entità padre l’intera collezione referenziata. Le Entity District, Province e SystemInformation Esistono vari casi in cui una Entity del sistema presenta un mapping analogo a quello descritto in precedenza, con una o più associazioni di tipo <many-to-one>, ciascuna scelta in base ad un criterio ben preciso:  La Entity District con la Entity Province di modo da poter associare diversi comuni ad ogni provincia;  La Entity Province con la Entity Region di modo da poter associare diverse province ad una sola regione;  La Entity SystemInformation con la Entity User di modo da poter associare diversi dati riguardanti il sistema ad un solo utente autore;
  • 11.
    11 Le Entity Carte CarLine Stabilire il tipo di associazione tra la classe Cart e CartLine è stato tutt’altro che semplice. Abbiamo scelto di optare, infine, per un’associazione <many-to-one> all’interno del mapping di CartLine in cui la FK all’interno della tabella di CartLine fa riferimento alla PK della tabella di Cart. <hibernate-mapping> <class name="it.univaq.mwt.fastmarket.business.model.Cart" table="CARTS"> <id name="id" type="long"> <column name="ID" /> <generator class="sequence"> <param name="sequence">SEQUENCE_CARTS</param> </generator> </id> <property access="field" lazy="false" name="name" type="java.lang.String"> <column name="NAME" not-null="true" /> </property> <property access="field" lazy="false" name="totalPrice" type="float"> <column name="TOTALPRICE" /> </property> <many-to-one name="user" class="it.univaq.mwt.fastmarket.business.model.User" access="field" fetch="join"> <column name="USER_ID" unique="true" not-null="true" /> </many-to-one> </class> </hibernate-mapping> <hibernate-mapping> <class name="it.univaq.mwt.fastmarket.business.model.CartLine" table="CARTLINES"> <id name="id" type="long"> <column name="ID" /> <generator class="sequence"> <param name="sequence">SEQUENCE_CARTLINES</param> </generator> </id> <property access="field" lazy="false" name="quantity" type="int"> <column name="QUANTITY" not-null="true" /> </property> <many-to-one name="cart" class="it.univaq.mwt.fastmarket.business.model.Cart" access="field" fetch="join"> <column name="CART_ID" not-null="true" /> </many-to-one> <many-to-one name="product" class="it.univaq.mwt.fastmarket.business.model.Product" access="field" fetch="join"> <column name="PRODUCT_ID" not-null="true" /> </many-to-one> </class> </hibernate-mapping> La scelta del mapping <many-to-one> tra Cart e User serve in realtà ad esprimere un’associazione one-to-one unidirezionale per la quale all’interno della tabella CARTS viene inserita la FK che referenzia la PK della tabella associata USERS. Alla colonna dentro a CARTS dove viene inserita la FK verso USERS è stato necessario, tuttavia, specificare la clausola unique=”true” per far sì che Hibernate legga realmente la relazione come one-to-one. Invece, la scelta del mapping <many-to-one> tra CarLine e Cart è dettata dal fatto che, inserendo la FK all’interno della tabella CARTLINES, riusciamo ad assicurarci che una o più istanze di CartLine siano associate ad un solo carrello; per rafforzare tale unicità, abbiamo anche gestito il lancio dell’eccezione di violazione del vincolo di chiave univoca all’interno dell’applicazione nel caso in cui l’utente cerchi di associare una stessa CartLine a più carrelli.
  • 12.
    12 In realtà, essendoun’applicazione e-commerce che consente ad ogni utente di avere un solo carrello, abbiamo pensato che l’utente registrato eliminerebbe dal carrello soltanto una cartLine per volta perciò non si produrrebbe mai l’eventualità in cui un utente tenta di eliminare direttamente il carrello riempito dalle cartLines (eventualità che viene, ad ogni modo, gestita nel backend dell’app nel caso in cui un dipendente del supermercato cerchi di farlo). Infine, all’interno di CartLine abbiamo dichiarato un'altra associazione <many-to-one> per creare un riferimento verso la Entity Product, dato che svariate righe del carrello possono contenere uno stesso prodotto. Le Entity Order, Booking e Delivery Nel caso di Order, Booking e Delivery le Entity sono legate tramite una generalizzazione, per la quale Booking e Delivery estendono da Order. Tra le varie strategie a disposizione per mappare l’ereditarietà, abbiamo scelto quello che W. Keller ha definito “One Class One Table”, ovvero abbiamo mappato sia le proprietà della classe astratta che le proprietà delle classi concrete creando così 3 tabelle diverse; ciò significa che ciascuna tabella contiene gli attributi della rispettiva classe. In questo caso, dato che Delivery e Booking ereditano anche la chiave primaria, il join tra le tabelle viene attuato tramite la colonna della PK all’interno della tabella padre. Abbiamo deciso di optare per questo mapping poiché, tra le varie opzioni disponibili, è ottimale a livello di consumo di spazio nel DB e questo aspetto è per noi fondamentale dato che nel nostro caso la classe Booking presenta svariati attributi. A livello di mapping xml è stato, dunque, generato un unico mapping per la classe padre Order con all’interno specificati i tag <joined-subclass> con internamente presente l’attributo extends. <hibernate-mapping> <class abstract="true" name="it.univaq.mwt.fastmarket.business.model.Order" table="ORDERS"> <id name="id" type="long"> <column name="ID" /> <generator class="sequence"> <param name="sequence">SEQUENCE_ORDERS</param> </generator> </id> <property access="field" lazy="false" name="creationDate" type="date"> <column name="CREATIONDATE" not-null="true" /> </property> <property access="field" lazy="false" name="totalPrice" type="float"> <column name="TOTALPRICE" not-null="true" /> </property> <many-to-one name="cart" class="it.univaq.mwt.fastmarket.business.model.Cart" access="field" fetch="join"> <column name="CART_ID" not-null="true" /> </many-to-one> <joined-subclass name="it.univaq.mwt.fastmarket.business.model.Booking" extends="it.univaq.mwt.fastmarket.business.model.Order" table="BOOKINGS"> <key> <column name="ID" not-null="true" /> </key> <property access="field" lazy="false" name="expirationDate" type="date"> <column name="EXPIRATIONDATE" not-null="true" /> </property> </joined-subclass> <joined-subclass name="it.univaq.mwt.fastmarket.business.model.Delivery"
  • 13.
    13 extends="it.univaq.mwt.fastmarket.business.model.Order" table="DELIVERIES"> <key> <column name="ID" not-null="true"/> </key> <property access="field" lazy="false" name="dispatchDate" type="date"> <column name="DISPATCHDATE" not-null="true" /> </property> <property access="field" lazy="false" name="deliveryCosts" type="float"> <column name="DELIVERYCOSTS" not-null="true" /> </property> <property access="field" lazy="false" name="deliveryType" type="java.lang.String"> <column name="DELIVERYTYPE" /> </property> </joined-subclass> </class> </hibernate-mapping> Le Entity Product, Grocery e NonGrocery Un altro caso di generalizzazione mette in relazione le Entity Product, Grocery e NonGrocery. Il mapping è analogo a quello appena descritto tranne per il fatto che la classe padre ha anche una FK verso Brand e una verso Category e la classe figlia Grocery ha una FK verso IntoleranceCategory. Anche in questo caso è stato creato un unico mapping per la Entity Product con all’interno le entità figlie Grocery e NonGrocery, che vengono dichiate tramite l’attributo <joined- subclass>. <hibernate-mapping> <class name="it.univaq.mwt.fastmarket.business.model.Product" table="PRODUCTS"> <id name="id" type="long"> <column name="ID" /> <generator class="sequence"> <param name="sequence">SEQUENCE_PRODUCTS</param> </generator> </id> <property access="field" lazy="false" name="name" type="java.lang.String"> <column name="NAME" not-null="true" unique="true" /> </property> <property access="field" lazy="false" name="description" type="java.lang.String"> <column name="DESCRIPTION" /> </property> <property access="field" lazy="false" name="price" type="float"> <column name="PRICE" not-null="true" /> </property> <property access="field" lazy="false" name="stock" type="int"> <column name="STOCK" not-null="true" /> </property> <many-to-one name="brand" class="it.univaq.mwt.fastmarket.business.model.Brand" access="field" fetch="join"> <column name="BRAND_ID" not-null="true" /> </many-to-one> <many-to-one name="category" class="it.univaq.mwt.fastmarket.business.model.Category" access="field" fetch="join"> <column name="CATEGORY_ID" not-null="true" /> </many-to-one> <property access="field" lazy="false" name="path" type="java.lang.String"> <column name="PATH" /> </property> <joined-subclass name="it.univaq.mwt.fastmarket.business.model.Grocery" extends="it.univaq.mwt.fastmarket.business.model.Product" table="GROCERIES"> <key> <column name="ID" not-null="true" /> </key>
  • 14.
    14 <property access="field" lazy="false"name="expirationDate" type="date"> <column name="EXPIRATIONDATE" not-null="true" /> </property> <property access="field" lazy="false" name="pricePerKg" type="float"> <column name="PRICEPERKG" /> </property> <property access="field" lazy="false" name="pricePerLt" type="float"> <column name="PRICEPERLT" /> </property> <many-to-one name="intoleranceCategory" class="it.univaq.mwt.fastmarket.business.model.IntoleranceCategory" fetch="join"> <column name="INTOLERANCECATEGORY_ID" /> </many-to-one> </joined-subclass> <joined-subclass name="it.univaq.mwt.fastmarket.business.model.NonGrocery" extends="it.univaq.mwt.fastmarket.business.model.Product" table="NONGROCERIES"> <key> <column name="ID" not-null="true" /> </key> <property access="field" lazy="false" name="scopeOfUse" type="java.lang.String"> <column name="SCOPEOFUSE" /> </property> </joined-subclass> </class> </hibernate-mapping> Le Entity User e Role Nel caso di User e Role, abbiamo deciso di definire una relazione bidirezionale che permette di navigare verso entrambi i lati dell’associazione. In questo modo, ci è possibile ottenere con facilità sia tutti gli utenti a cui sono stati associati uno o più ruoli che tutti i ruoli che sono stati associati ad uno o più utenti. A livello di mapping, le due classi entity vengono messe in relazione dichiarando una collezione di oggetti sia all’interno del mapping di User che in quello di Role. <hibernate-mapping> <class name="it.univaq.mwt.fastmarket.business.model.User" table="USERS"> <id name="id" type="long"> <column name="ID" /> <generator class="sequence"> <param name="sequence">SEQUENCE_USERS</param> </generator> </id> <property access="field" lazy="false" name="username" type="java.lang.String"> <column name="USERNAME" not-null="true" unique="true" /> </property> <property access="field" lazy="false" name="password" type="java.lang.String"> <column name="PASSWORD" not-null="true" /> </property> <property access="field" lazy="false" name="firstName" type="java.lang.String"> <column name="FIRSTNAME" not-null="true" /> </property> <property access="field" lazy="false" name="middleName" type="java.lang.String"> <column name="MIDDLENAME" /> </property> <property access="field" lazy="false" name="lastName" type="java.lang.String"> <column name="LASTNAME" not-null="true" /> </property> <property access="field" lazy="false" name="gender" type="char"> <column name="GENDER" /> </property> <property access="field" lazy="false" name="mobilePhone" type="java.lang.String"> <column name="MOBILEPHONE" /> </property> <property access="field" lazy="false" name="telephone" type="java.lang.String"> <column name="TELEPHONE" /> </property>
  • 15.
    15 <property access="field" lazy="false"name="email" type="java.lang.String"> <column name="EMAIL" not-null="true" /> </property> <set name="roles" table="USERS_ROLES" inverse="false" access="field" lazy="true"> <key> <column name="USER_ID" not-null="true" /> </key> <many-to-many class="it.univaq.mwt.fastmarket.business.model.Role" column="ROLE_ID" /> </set> </class> </hibernate-mapping> <hibernate-mapping> <class name="it.univaq.mwt.fastmarket.business.model.Role" table="ROLES"> <id name="id" type="long"> <column name="ID" /> <generator class="sequence"> <param name="sequence">SEQUENCE_ROLES</param> </generator> </id> <property access="field" lazy="false" name="name" type="java.lang.String"> <column name="NAME" not-null="true" /> </property> <property access="field" lazy="false" name="description" type="text"> <column name="DESCRIPTION" /> </property> <set name="users" table="USERS_ROLES" cascade="save-update" inverse="true" access="field" lazy="true"> <key> <column name="ROLE_ID" /> </key> <many-to-many class="it.univaq.mwt.fastmarket.business.model.User" column="USER_ID" /> </set> </class> </hibernate-mapping> Analizzando il mapping di User, notiamo che alla property username è specificato l’attributo unique=”true”. Esso è stato applicato anche ad altre proprietà presenti in altri mapping (es. alla proprietà name di Brand) e permette di generare istruzioni DDL di vincolo di unicità alla colonna corrispondente nel DB. Per quanto riguarda, invece, le collezioni di oggetti presenti all’interno di entrambi i mapping User e Role:  abbiamo scelto di dichiarare un set di oggetti poiché corrisponde all’interfaccia java.util.Set e ci siamo resi conto che fa al caso nostro poiché, oltre a preservare l’ordine degli oggetti, non ammette duplicati;  abbiamo inserito l’attributo cascade=”save-update” cosicché Hibernate navighi l’associazione ogniqualvolta un oggetto viene salvato o modificato e renda persistenti le istanze transienti o le modifiche apportate alle istanze fuori dal contesto transazionale;  abbiamo dichiarato l’attributo inverse=”true” all’interno del set users dentro al mapping di Role per informare Hibernate quale lato dell’associazione non deve sincronizzare con il DB. In questo caso viene istruito Hibernate di ignorare qualsiasi cambiamento apportato alla collezione users e, al contrario, di sincronizzare con il database
  • 16.
    16 soltanto la collezioneroles tramite la tabella associativa. Facendo ciò, evitiamo di eseguire due volte lo stesso statement SQL sulla stessa FK.  Abbiamo optato per l’inserimento di lazy=”true” che è in realtà la strategia di default, di modo da non richiedere ad Hibernate di caricare dal database oltre all’istanza della Entity anche la collezione di oggetti associati.
  • 17.
    17 4. L’architettura delsistema Per realizzare l’applicazione FastMarket, abbiamo optato per un’architettura J2EE nella quale i vari componenti architetturali si trovano sulla stessa JVM ed interagiscono tra loro a livello “locale” al fine di far funzionare l’intero sistema. Più precisamente, abbiamo un modello architetturale nel quale vi è da un lato un client web component locale che gestisce la parte di presentazione, da un altro i componenti lato server che implementano l’intera logica di business e da un altro ancora un EIS (Enterprise Information System) che fornisce i dati, che in questo caso è un DB Oracle. Il web component che si occupa sia della lookup JNDI verso l’interfaccia locale degli EJB che della gestione della parte web (ovvero del rendering dell’interfaccia), la logica di business (ovvero gli EJB) e il modello di dominio vengono impacchettati all’interno di un file con estensione .ear ed assemblati all’interno dell’application server Glassfish. Una volta che l’.ear viene deployato ed eseguito su Glassfish il ContextLoaderListener di Spring legge il file root-context e, in base ai parametri specificati internamente, fa una lookup JNDI verso gli EJB che si trovano dentro all’EJB container dell’application server. La Dispatcher Servlet si occupa invece di costruire i componenti controller per gestire le richieste http e, per fare ciò, inietta all’interno degli stessi ogni interfaccia remota che trova annotata con @Autowired. Per essere più precisi, il client invoca uno o più metodi dell’interfaccia locale annotata con @Local che a sua volta richiama i relativi metodi dichiarati nel session bean (EJB) stateless – ovvero che non tiene memoria della transazione svolta – annotato appunto con @Stateless. All’interno di ciascun bean viene dichiarata la relativa interfaccia locale tramite l’annotazione @Local(NomeInterfaccia.class) mentre ogni metodo viene annotato tramite @TransactionAttribute(TransactionAttributeType.REQUIRED) per indicare che se al client che invoca un metodo del bean è associato un determinato contesto transazionale, allora il container richiamerà il metodo del bean all’interno di tale contesto. Inoltre, l’annotazione @TransactionManagement(TransactionManagementType.CONTAINER) posta sopra alla classe indica che tutti i metodi vengono gestiti dal container. La gestione della persistenza dati viene, invece, delegata al framework Hibernate tramite l’utilizzo delle sessioni e dei mapping xml object/relational contenuti all’interno del modello di dominio mentre il servizio di connessione al DB Oracle è fornito dall’application server tramite il connettore JDBC. Alla fine di ogni processo di elaborazione delle richieste http, l’applicazione web eseguita su Glassfish gestisce la parte di vista da renderizzare all’utente tramite la tecnologia per implementare le view nota come JavaServer Pages. In realtà, però, la costruzione della vista viene delegata al framework di templating Apache tiles il quale assembla diversi frammenti (jsp) per costruire l’intera pagina web.
  • 18.
    18 Di seguito presentiamograficamente l’architettura del sistema finora descritto: