Lucene e Wordnet

Lucene e Wordnet: Espansione della ricerca con sinonimi G.Morreale
Introduzione:
Negli articoli predenti abbiamo visto come usare lucene per effettuare delle ricerche semplici e delle ricerche con correzioni ortografiche.
In questo articolo invece voglio mostravi l'uso di wordnet al fine di espandere le proprie ricerche sui sinonimi di una determinata parola.
Se ad esempio viene effettua una ricerca sul nostro indice per la parola "big" e la ricerca non và a buon fine possiamo rieffettuare la ricerca espandendola per tutti i sinonimi di "big", ovvero: "large, prominent, heavy, great" etc.
WordNet
Per far ciò bisogna innanzitutto avere wordnet.
Wordnet non è altro che un insieme di informazioni relative alle parole di un vocabolario (in questo caso inglese) correlate tra loro attraverso il loro significato.(Per affrofondire: http://bit-cafe.blogspot.com/2008/04/wordnet.html)
Tali informazioni sono liberamente scaricabili da:http://wordnet.princeton.edu/
Una volta scaricato l'archivio esistono diversi file, alcuni di questi con estensione .pl.
Tali file sono in formato prolog.
L'unico file che ci interessa ai fini dell'articolo è win_s.pl
NOTA:
Esistono i corrispettivi archivi in altre lingue, ma ognuno ha una sua licenza:http://www.globalwordnet.org/gwa/wordnet_table.htm
Indice Lucene di Wordnet
Al fine di sfruttare le potenzialità di lucene sui dati scaricati da wordnet, è necessario costruire un indice lucene con i dati appena scaricati.
Per far ciò esiste un sorgente java all'interno del pacchetto di libreria di lucene che svolge tale compito
Scarichiamo i sorgenti di lucene e cerchiamo il file Syns2Index.java all'interno del package org.apache.lucene.wordnet situato nella directory contrib
Compiliamo tale file e lo eseguiamo a linea di comando passando 2 valori:
1) path dove memorizzare l'indice contenente la coppia parola - sinonimi
2) la path dove si trova il file win_s.pl
Dopo pochi secondi verrà creato l'indice lucene.
Il field indicizzato è "word"(Equivalente a Syns2Index.F_WORD), syn(Equivalente a Syns2Index.F_SYN) invece è il campo contenente i sinonimi.
Espansione della Query
All'interno dello stesso package in cui è presente Syns2Index, è presente un altra classe chiamata SynExpand.
Tale classe è di grande aiuto per l'espansione della query.
Infatti senza conoscere la struttura dell'indice creato partendo da wordnet, il metodo statico expand della classe SynExpand
riesce a calcolare una nuova query considerando i sinonimi della stringa passata come valore di input.
I parametri di input del metodo statico SynExpand.expand prevedono:
  • la stringa da espandere
  • Un oggetto Searcher inizializzato a partire dall'indice creato su wordnet
  • Un oggetto Analyzer
  • La String contenente il field che nella costruzione della query espansa verrà considerato come field di ricerca.
  • un float per il boosting dei sinonimi.
Il boast serve per dare un peso ai sinonimi rispetto alla parola principale.
es.
intendo effettuare una ricerca su big.
Prima di verificare se esiste nel mio indice, calcolo la ricerca espansa e indico come fattore di boost 0.5f in modo che la query espansa darà meno importanza ai sinonimi, ottenendo un risultato simile a questo:
contents:big contents:adult^0.5 contents:bad^0.5 contents:bighearted^0.5 contents:boastful^0.5 contents:boastfully^0.5 contents:bounteous^0.5 contents:bountiful^0.5 contents:braggart^0.5 contents:bragging^0.5 contents:braggy^0.5 contents:crowing^0.5 contents:enceinte^0.5 
Conclusione
Con pochi e semplici passi è possibile potenziare il proprio motore di ricerca avvalendosi di query che tengono conto dei sinonimi dei termini che costituiscono una query ricerca.
Riferimenti:
http://wordnet.princeton.edu/obtain
http://www.tropo.com/techno/java/lucene/wordnet.html

Differenza tra Servlet e JSP

Breve spiegazione circa le differenze tra JSP e Servlet
G.Morreale

Introduzione:

Spesso nei forum una domanda molto ricorrente da parte di chi inizia ad approdare al mondo Java EE, è proprio questa:

"Chè differenza c'è tra Servlet e JSP? Quando usare l'una e quando l'altra tecnologia?"

Cercherò di di dare indicazioni utili a trovare questa risposta.
Ovviamente il modo migliore per capire l'essenza delle differenze è studiare singolarmente le singole tecnologie:



Breve panoramica sulle due tecnologie:

Le Servlet

All'interno delle api java ee, esiste un classe astratta chiamata HttpServlet
Questa classe è alla base di ogni servlet che il programmatore intende realizzare.

Questa classe di per se fornisce delle funzionalità per la gestione delle diverse tipologie di richieste http: (HEAD, GET, POST, PUT, DELETE).
Ad esempio, l'HttpServlet, inizializza gli oggetti HttpServletRequest e HttpServletResponse, al fine di consentire al programmatore di ritrovare la rappresentazione della richiesta e della risposta http (headers, body, etc.) già strutturata all'interno di alcuni oggetti.

Tale funzionalità ovviamente devono essere completate al fine di generare un output che risponda alle nostre esigenze. (ad esempio realizzare una servlet che stampi la scritta "hello world").

Al fine di raggiungere questo obiettivo di crea una classe che estende HttpServlet.
L'estensione di tale classe migliorerà ad esempio il metodo doGet al fine stampare a video la scritta "hello world"(operazione che di base HttpServlet non esegue)!!

Esempio di servlet

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

//Estensione dell'oggetto HttpServlet: EREDITARIETA'
public class helloWorldServlet extends HttpServlet {

//...eventuali variabili di classe

    //Override del metodo doGet per generare l'output desiderato
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {

// Definisco il tipo MIME della risposta http.
response.setContentType("text/html; charset=ISO-8859-1");

        //ottengo da response l'oggetto che mi consente di scrivere sull'output
PrintWriter out = response.getWriter();

//Scrivo l'html di output
        out.println("<html>");
out.println("<body>");
        out.println("<h1>hello world " + new Date().toString() + "</h1>");
out.println("</body>");
out.println("</html>");
}
}


Le JSP

JSP è l'acronimo per Java Server Pages.
Le JSP sono una tecnologia che consente allo sviluppatore di combinare codice html (o comunque codice che riguarda la presentazione della pagina) e codice java in modo più agevole, basandosi sui dei tag.

Le JSP vengono compilate e trasformate in Servlet.
Quindi la JSP non è altro che una diversa rappresentazione di una servlet.
Rappresentazione che rende più semplice l'integrazione fra presentazione e codice java.

Esempio di servlet

Il codice precedemente scritto al fine di generare un semplice hello world nel caso di una JSP diventerebbe:

<%@ page contentType="text/html;charset=ISO-8859-1"%>
<html>
<body>
<h1>hello world<%=new Date().toString()%></h1>
</body>
</html>


Conclusione: La differenza!?
Ovviamente vi invito ad approfondire riguardo le singole tecnologie in quanto questo articolo ha il solo scopo di introdurre in modo molto conciso l'essenza di Servlet e JSP.
Tieniamo conto anche che la JSP nasce la presentazione dei dati, all'interno della JSP infatti bisogna ridurre al massimo la presenza di codice java, soprattutto codice che non riguarda la presentazione dei dati.
Se devo ad esempio interrogare un db, la jsp è uno dei posti in cui non è idoneo fare la connessione e richiesta verso il database.
Tale operazione infatti viene delegata alle servlet o meglio ancora in caso di progetti più strutturati ad altre tecnologie (es. ejb).

Alla JSP bisogna passare l'informazione già estratta ed elaborata, pronta il più possibile per essere stampata a video.

La Servlet invece è il luogo adatto per gestire il collegamento tra la JSP e la logica di business.

Vi invito a leggere anche questi due miei articoli, dove spiego come realizzare un applicazione che coinvolge JSP, Servlet ed EJB secondo il design pattern MVC.

http://programmaremobile.blogspot.com/2008/07/mvc-java-ee-mvc-un-design-pattern-una.html
http://programmaremobile.blogspot.com/2008/10/java-ee-un-esempio-su-come-usare-ejb-e.html#links





MySQL VS Oracle XE

Oracle XE vs MySQL
G.Morreale

Introduzione:

Dopo aver installato e provato(vedi precedenti articoli) la versione Express di Oracle è arrivato il momento di vedere se le prestazioni sono migliori o meno rispetto al noto dbms MySQL.

Scenario

Allora recupero la mia solita applicazione Java EE di test, costituita da un modulo EJB e un modulo WEB.
Il modulo EJB dotato di un session bean e un entity bean.

L'entity bean mappa la tabella esempiotable costituita da due colonne (id (INTEGER AUTOINC.), testo(STRING)).
Il persistence provider utilizzato è hibernate.
L'entitymanager utilizzato è un JTA Entity Manager ottenuto tramite dependency injection.
Il modulo Web è dotato di una servlet che ha il compito di richiamare il session bean presente nel modulo EJB.

Il test è una comparazione delle performance sia per quanto riguarda le operazioni di inserimento sul db che per ciò che concerne l'estrazione dei dati(SELECT).

Quindi il session bean(e relativa interfaccia locale) è dotato di due metodi:

  • Uno per l'inserimento di 'n' entityBean

    public void batchInsert(int nRows)
    {
        for (int i = 0; i < nRows; i++)
        {
            em.persist(new Esempiotable());            
        }
    }

  • l'altro per l'estrazione di tutti gli entityBean corrispondenti a tutte le righe della tabella di test

    public void listAll()
    {                
        List<Esempiotable> list = em.createNamedQuery("Esempiotable.ALL").getResultList();
        for (Esempiotable t: list)
        { System.out.println(t.getId());}        
    }


L'ambiente di sviluppo utilizzato è netbeans 6.1 dal quale sfrutterò il profiler per raccogliere i risultati ottenuti.
L'application server utilizzato è Glassfish V2.

Driver JDBC Mysql: mysql-connector-java-5.1.6-bin.jar
Driver JDBC Oracle:ojdbc14.jar

In Mysql storage engine utilizzato: "Inno DB"

L'Entity Bean

L'entity bean come già accennato è il mapping di una semplice tabella dotata di una colonna integer autoincrement e una colonna di testo.
Per cui il codice (valido per il mapping nei confronti di mysql) è:


@Entity
@Table(name = "esempiotable")
@NamedQueries(
{
   @NamedQuery(name = "Esempiotable.ALL", query = "SELECT e FROM Esempiotable e")
})
public class Esempiotable implements Serializable {
    private static final long serialVersionUID = 1L;
        
    @Id   
    @GeneratedValue(strategy=GenerationType.IDENTITY)    
    @Column(name = "id", nullable = false)
    private Integer id;
    
    @Column(name = "testo", nullable = true)
    private String testo;
//...omessi metodi accessor e costruttore


Tale mapping funziona correttamente su MySQL, ma utilizzandolo con Oracle si ottiene il seguente errore:

...
Caused by: java.lang.IllegalArgumentException: Dialect does not support identity key generation
        at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:223)
        at com.sun.enterprise.util.EntityManagerWrapper.persist(EntityManagerWrapper.java:440)
...

Ciò è dovuto al fatto che l'autoincrement di Oracle è gestito attraverso le sequenze, ovvero oggetti del db che indicano l'algoritmo di generazione e incremento della chiave primaria.
Quindi  "GenerationType.IDENTITY" deve essere sostituito con "GenerationType.SEQUENCE"

Tale modifica non è sufficente infatti si ottiene il seguente errore:

Caused by: javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not get next sequence value

L'errore si verifica perchè hibernate non è stato istruito circa quale sequence utilizzare.
Quindi attraverso SQLDeveloper andiamo a leggere il nome del sequence generator e indichiamolo attraverso le opportune annotation:

In definitiva il codice

@GeneratedValue(strategy=GenerationType.IDENTITY)  

Verrà sostituito con il seguente nel momento in cui il test dovrà essere eseguito su dbms oracle xe.

@GeneratedValue(strategy=GenerationType.AUTO, generator="ESEMPIOTABLE_ID_SEQ")
@SequenceGenerator(name="ESEMPIOTABLE_ID_SEQ", sequenceName="ESEMPIOTABLE_ID_SEQ")    

nota: E' stato utilizzato il metodo AUTO piuttosto che SEQUENCE per maggiore portabilità rispetto a futuri cambiamenti.


Gli Inserimenti

(Per la lettura dei risultati leggere il selftime dei metodi batchInsert o listAll)

Il primo test prevede un inserimento di 100.000 entity all'interno del db:
Vediamo subito i risultati:

Oracle XE


MySQL
Ripetiamo il test con 500.000

Oracle XE

MySQL

L'estrazione
Analizziamo i risultati di estrazione di 500.000 elementi:
Oracle XE
MySQL

Conclusione

Questi test sono da prendere con le pinze in quanto l'architettura del sistema dbms, application server, sistema operativo e quant'altro non è quella relativa a un sistema in "produzione".

Il setup e il tuning dei due dbms è quello standard, ovvero non è stata apportata alcuna modifica dopo l'installazione alla configurazione dei relativi dbms.

Inoltre i test sono relativi solo a 2 case e non prevedendo altri scenari tipici di un applicazione.

Fatte queste premesse, possiamo dire che mysql sia in fase di inserimento che in fase di estrazione ha dato dei risultati nettamente migliori.

Personalmente questi risultati nonostante siano basati su delle prove concrete mi lasciano un pò perplesso, in quanto mi aspetto di più lato Oracle. Se qualcuno che conosce bene Oracle XE può darci qualche consiglio per rifare i test in virtù di alcuni settaggi o accorgimenti ben venga.


Oracle XE: Importare DB da MySQL

Oracle XE - Come effettuare la migrazione da MySQL
G.Morreale

Introduzione:

Nel precedente articolo ho cercato di introdurre l'uso di SqlDeveloper utilizzando una connessione verso Oracle XE.
Adesso vediamo quali sono i vari passi da seguire per importare uno o più database dal mysql a oracle xe utilizzando oracle SqlDeveloper.


Configurazione Supporto MySQL

Selezionare: Tools -> Preferences -> Database -> Thirdy Party JDBC Driver -> Add Entry ..


Scegliere sul disco il file .jar che contiene il connettore jdbc di mysql (Connector J - scaricabile da http://dev.mysql.com/downloads/connector/j/5.0.html )
Salvare i settaggi.


Creazione connessione MySQL

Adesso tornando sul pannello principale Selezionare:
Connections -> New Connections -> Mysql Tab(prima non c'èra!!) -> inserire i parametri della connessione al db mysql.
salviamo la connesione come MySQL.


nota:
durante il processo di migrazione sqldeveloper, nonostante la connessione mysql specifichi un determinato database, importa tutti i db su cui l'utente usato nella connessione ha i relativi permessi.
Quindi al fine di importare un singolo db conviene creare in mysql un utente ad hoc per il database da importare.

Una volta creata la connesione mysql e dopo essersi connessi è possibile navigare ed utilizzare sqldeveloper come client mysql(un alternativa al mysql querybrowser)


Creazione nuova connessione ORACLE per la migrazione

Prima di effettuare la migrazione, è consigliabile(non obbligatorio) creare un utente(ad. es. MY_TEST) oracle diverso da system, su cui importeremo il db mysql.

Per far ciò selezionare other user -> create user e assegnare tutti i permessi possibili (Grant all, admin all, default all) (è un test!!)

Adesso creiamo una nuova connessione oracle sull'utente appena creato e la salviamo con il nome ORACLE MY TEST.

Riepilogando avremo quindi 3 connessioni

  1. MYSQL - Connessione verso il db (o i DB) da importare
  2. ORACLE SYSTEM - Connessione con l'utente system che non useremo ai fini della migrazione
  3. ORACLE MY TEST - Connessione con l'utete MY_TEST sul quale effettueremo l'importazione.

La Migrazione

Adesso per effettuare la migrazione bisogna selezionare:

Migration -> QuickMigrate -> Scegliere la connessione mysql(sorgente) -> Scegliere la connesione oracle(destinazione) -> Effettuare il Verify -> Scegliere se migrare solo tabelle o tabelle/dati -> Finish!!


All'intero dell'utente oracle relativo alla destinazione indicata in fase di migrazione ci saranno gli elementi (tabelle, viste etc.) importati direttamente da mysql.

Conclusione

Il processo di migrazione risulta essere abbastanza semplice e lineare, almeno per ciò che riguarda database "semplici".
Nel prossimo articolo invece ho intenzione di scrivere circa un confronto di prestazioni tra mysql ed oracle xe utilizzandoli con hibernate su inserimenti e select relativi a migliaglia di righe.


Oracle XE - Uno sguardo verso un dbms Oracle free

Oracle XE - Primi passi
G.Morreale

Introduzione:

Oracle XE è la versione free del re dei DBMS.
Con il seguente articolo diamo un occhio ai primi passi da muovere dopo l'installazione del pacchetto Oracle XE.

Limitazioni di Oracle XE

Prima di andare avanti vorrei però far presente che tale prodotto non è opensource come mysql o altri dbms e nella versione sono presenti le seguenti limitazioni:

MASSIMO 1 GB DI MEMORIA RAM USATA
MASSIMO 1 CPU
MASSIMO 4 gb di dati

Quindi anche se la macchina è dotata di 4 processori e 16 GB di Ram Oracle Xe utilizzerà solo una parte delle risorse secondo i limiti appena elencati.

Sql Developer

L'installazione di Oracle XE prevede un http listener in grado di rispondere sulla porta 8080.
L'applicazione installata permette di effettuare gran parte delle operazioni del dbms, è semplice e intutitiva.
Per darci un occhio provare a richiamare dal browser il seguente url: http://127.0.0.1:8080/apex/

Nonostante ciò ho preferito installare ed usare un tool gratuito fornito da Oracle che consente una gestione più completa del dbms oracle e non solo.

La Versione analizzata è la 1.5.1

La prima connessione

Non appena installato e avviato Sql Developer, bisogna aggiungere una nuova connessione per poter operare sul dbms.
Con il tasto destro del mouse è possibile aprire il menu a tendina in corrispondenza dell'icona "Connections" e scegliere "New Connection".

La form è già predisposta, basta indicare il nome della connessione, l'utente system e la password scelta in fase di installazione.
Si salva la connessione, si effettua un test di connettività, e se tutto è ok si procede con la connessione vera e propria.

Consiglio di salvare questa connessione come ORACLE SYSTEM, in modo da distinguerla con altre che creeremo successivamente.

Oggetti del DBMS

Una volta connessi compaiono una serie di oggetti, di seguito elenco e descrivo brevemente le funzionalità di tali elementi:




  • Tables - Le tabelle!
  • Views  - Tabelle virtuali
  • Indexes - Indici presenti nelle colonne delle varie tabelle
  • Packages - Possono contenere functions e/o procedures, è un elemento che serve a raggruparli.
  • Procedures - Procedure PL/SQL (Linguaggio di programmazione per dbms ORACLE)
  • Functions - Funzione in PL/SQL a differenza della precedente ritorna un valore
  • Queues - Code usate dalla tecnologia oracle AQ (Oracle Streams Advanced Queueing)
  • Queues Tables - Tabelle che contentongo i dati utilizzati dalle code.
  • Triggers - Blocchi di codice PL/SQL spesso associati a tabelle o schema. (implementabili in PL/SQL o Java), vengono eseguiti al verificarsi di determinate condizioni.
  • Types - Tipi di dato definiti dall'utente, da utilizzare ad esempio nella definizione di colonne.
  • Sequences - Algoritmi per la generazione automatica di primary key. (un esempio banale è l'autoincrement)
  • Materialized Views - Contiene i risultati di una view.
  • Materialized Views Logs - log dei cambiamenti sulle Materialized Views
  • Synonyms(Public) - Contiene una serie di alias, per chiamare in modo diverso i vari oggetti del db (tabelle, view, sequences etc. etc.)
  • Database Links(Public) - Contiene link verso database NON oracle, per consentire l'accesso mediante dbms oracle a database, quindi tabelle, viste etc NON oracle.
  • Directories - Contiene gli alias per directory del filesystem nel quale si trovano i LOB(dati in formato binario) o i dati esterni alle tabelle.
  • Java - Raccoglie eventuali sorgenti java
  • XML Schemas - Contiene gli schemi xml che definisco la struttura dei documenti xml presenti nel dbms
  • Recycle bin - Permette di recuperare oggetti del db sottoposti a comando drop
  • Other Users -  Contiene gli utenti definiti nel dbms

Riguardo al pannello presente nell'immagine di sopra, tutti gli elementi da "tables" fino a "other users" sono relativi all'utente con il quale si è effettuata la connessione.

Cliccando invece sul nodo "other users" e aprendo il nodo di uno degli utenti presenti verranno visualizzati tutti gli oggetti (tables,views, indexes, etc.) relativi a quel particolare utente.

Creare uno schema

In Oracle XE, non è possible creare più di un db per installazione(1 db <-> 1 computer), però esiste il concetto di schema con il quale la limitazione diventa in alcuni casi inesistente.

Lo schema è una collezione di oggetti del database (tabelle, view, procedure etc.)
Ogni schema è di proprietà di almeno un utente che ha lo stesso nome dello schema.

Se ad esempio siamo loggati con l'utente system, la cartella tables(ma anche gli altri oggetti) è relativa allo schema di system.

Quindi se volessimo creare uno schema diverso, dobbiamo creare un nuovo utente per il nuovo schema da creare e poi agire su di esso.


Conclusione

Oracle XE sembra essere una valida alternativa ai dbms free usati a livello desktop.
Ovviamente bisogna usare il prodotto in modo molto più approfondito per capire dove sono realmente i limiti di questo dbms rispetto alle versioni non free e rispetto ai dbms free come mysql o postgresql.
Nel successivo post descriverò il processo per importare i db mysql all'interno di oracle xe

Hibernate: Caricare migliaia di righe

Hibernate: getResultList() vs ScrollableResults G.Morreale
Introduzione:
Se sul db ci sono migliaia di righe in una tabella e intendiamo caricare i corrispettivi entity(magari per effettuare un batch update), potremmo trovarci di fronte a delle scelte al fine di ottimizzare l'uso della memoria.
Le soluzioni adottate sono due.
  1. Usare un JTA EntityManager, richiamare una namedQuery e ottenere la collection dei risultati con getResultList() (richiamato sull'oggetto Query)
  2. Usare l'oggetto session di hibernate e appoggiarsi all'oggetto ScrollableResults per ottenere i risultati
Esempio
Supponiamo di avere il solito Entity Esempiotable(composto da due campi: int:id e string:testo)
Supponiamo che ci siano sul db 500.000 righe (per le quali hibernate genera 500.000 Entity)
La namedQuery utilizzata è la seguente:
@NamedQuery(name = "Esempiotable.ALL", query = "SELECT e FROM Esempiotable e")
Codice Soluzione 1:
public void listAll()
{
List<Esempiotable> list = em.createNamedQuery("Esempiotable.ALL").getResultList();
list.get(0);
}
Codice Soluzione 2:
public void scrollableResultTest()
{
Configuration configuration = new Configuration();
SessionFactory sf = configuration.buildSessionFactory();
Session ses = sf.openSession();
ScrollableResults res = (ScrollableResults) ses.getNamedQuery("Esempiotable.ALL").scroll();
res.next();
}
Quindi i metodi al confronto sono "getResultList" e "scroll".
Leggendo le javadoc non ci sono molti dettagli circa la funzionalità di tali metodi, il primo carica i risultati della query nella struttura dati List, il secondo invece permette di ottenere un iterator in grado di estrarre i risultati spostandosi attraverso dei cursori all'interno del result iterator stesso.
Quindi forse il metodo migliore per coglierne effettivamente le differenze è quello di lanciare il profiler e analizzare il consumo di memoria:
Nel caso di getResultList vengono allocati 2.839.152 B (immagine sopra)
Nel caso invece di scroll vengono allocati 12.840 B (immagine sotto)
Quindi è facile notare come il consumo di memoria nel caso di inizializzazione della List e dello ScrollableResult sia nettamente minore nel secondo caso.
nota:
Il supporto della scrollabilità dei risultati dipende dal driver jdbc sottostante.
E le performance?
Diamo un occhio all'immagine sopra, il metodo listAll() rappresenta la soluzione 1, mentre scrollableResultTest() rappresenta la soluzione 2.
Come è facile notare l'inizializzazione della seconda struttura dati è molto più veloce rispetto alla prima.
nota:
(il divario resta, anzi aumenta anche nel caso di invocazioni multiple dei metodi)
Però..
Se una volta ottenute le corrispettive strutture dati si vuole fare un ciclo al fine di operare sugli entity via via estratti dalla struttura i risultati si ribaltano:
(metodo eseguito su 500.000 rows)
Infatti estrarre i risultati dalla scrollableList richiede una quantità di memoria minore, nonchè un inizializzazione più veloce, ma in fase di estrazione risulta essere più lento.
Conclusione
Come in ogni caso la scelta della soluzione dipende dal contesto.
Se si intende risparmiare sulla memoria e penalizzare leggermente le performance si può optare per la soluzione "scrollable", altrimenti se non si hanno problemi di memoria (macchine con molta molta ram, su cui è configurato una max heap size) notevole allora si può caricare tutto in memoria e operare sui vari dati già estratti.
Tutto dipende..

Hibernate: Ottenere la Session

Hibernate: Come inizializzare una Session
G.Morreale

Introduzione:

Dovendo fare diversi esprimenti utilizzando hibernate, mi sono trovato davanti all'inizializzazione dell'oggetto Session di hibernate al fine di utilizzarlo al posto dell'EntityManager.

Il Codice

Passiamo direttamente a vedere il codice che commenterò di seguito passo per passo.

        AnnotationConfiguration configuration = new AnnotationConfiguration();
        configuration.addResource("META-INF/hibernate.cfg.xml");
        configuration.addPackage("entities");        
        configuration.addAnnotatedClass(entities.Esempiotable.class);       
        configuration.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");        
        configuration.setProperty("hibernate.connection.datasource", "esempioJNDI");        
        SessionFactory sf = configuration.buildSessionFactory();        
        Session ses = sf.openSession();   

La prima riga di codice serve a inizializzare la Configuration.

AnnotationConfiguration configuration = new AnnotationConfiguration();

Essa è la rappresentazione della configurazione di hibernate.
Tale configurazione spesso viene inserita all'interno di un file xml, per cui non appena inizializzato l'oggetto lo si può aggiornare con i settaggi presenti nel file xml di configurazione

configuration.addResource("META-INF/hibernate.cfg.xml");

Il metodo addResource è utilizzato quando il file di configurazione si trova nel classpath (ad es. dentro il jar)

Ma torniamo alla prima riga di codice: Perchè AnnotationConfiguration piuttosto che Configuration?
La scelta di questa specializzazione della classe è dovuta al mapping.
Hibernate è un ORM framework, quindi deve conoscere il mapping tra i dati relazionali (le tabelle ad esempio) e i corrispettivi entity.
Tale mapping può essere configurato in diversi modi

  • file xml di configurazione
  • settando le oppurtune proprietà sull'istanza di configuration
  • Attraverso le annotation

Se il progetto, come nel caso dell'esempio, ha un mapping effettuato attraverso annotation, si può aggiornare la configuration indicando il nome del package e/o della classe che ha le annotazioni che provvedono al mapping.

Istruzioni per indicare a hibernate dove trovare il mapping dell'Entity Esempiotable
configuration.addPackage("entities");        
configuration.addAnnotatedClass(entities.Esempiotable.class);

nota:
Nel caso in cui il mapping non è configurato si ottiene il seguente errore nello stack trace:
.... Caused by: org.hibernate.MappingException: ....

andiamo avanti:
Il dialect, se tale proprietà non viene settata hibernate genera un eccezione

..HibernateException: Hibernate Dialect must be explicitly set..

Ovviamente tale settaggio, come tutti gli altri, può essere inserito all'interno del file xml.

Se non si setta opportunamente il datasource si ottiene il seguente errore:

....The user must supply a JDBC connection.....

Quindi bisogna fornire a hibernate indicazioni circa le connessioni jdbc.
Esse di solito vengono settate attraverso le seguenti proprietà dal chiaro significato.

  • hibernate.connection.driver_class
  • hibernate.connection.url
  • hibernate.connection.username
  • hibernate.connection.password

etc. etc.

Ma nel caso, come l'esempio, in cui si vuole utilizzare una risorsa (JNDI) dell'application pool è possibile settare il JNDI name (e altre proprietà opzionali) per indicare a hibernate di estrarre la connessione attraverso la risorsa del container.
L'istruzione per far ciò è la seguente.
       
configuration.setProperty("hibernate.connection.datasource", "esempioJNDI");
        
Le ultime due istruzioni sono banali, esse servono una a creare una SessionFactory, ovvero un oggetto thread-safe in grado di generare session l'altra invece serve a generare una sessione, una unit of work, un'oggetto NON thread safe in grado di rappresentare la conversazione con il persistence context e il db.

        SessionFactory sf = configuration.buildSessionFactory();        
        Session ses = sf.openSession();   

Una volta ottenuto l'oggetto session si possono eseguire query, operazioni di find, merging, save etc. date un occhio alla javadoc di hibernate: http://www.hibernate.org/hib_docs/v3/api/org/hibernate/Session.html


Conclusione

Al fine di creare del codice il più ordinato possibile la configurazione dovrebbe risidiere nel file xml opportunamente costruito secondo le specifiche di hibernate.
Nonostante ciò ho voluto mostrare come creare una session aggiungendo alcune proprietà mancanti sull'istanza della configurazione.
Inoltre le proprietà mancanti non sono necessarie nel caso in cui si usa l'entityManager piuttosto che l'oggetto session.

Hibernate: Inserire migliaia di righe senza avere problemi di m

Hibernate: Inserimenti Batch senza OutOfMemoryException G.Morreale
Introduzione:
In questo breve articolo voglio mostravi appena due righe di codice per rendere persistenti migliaia di Entity senza intasare la memoria.
Nel titolo è presente il nome di hibernate.. in quanto è il framework più usato, ma la soluzione è valida anche per altri persistence provider.
Esempio
Supponiamo di voler eseguire il seguente codice di inserimento
Esempiotable t;
for (int i = 0; i < 500000; i++)
{
//Creazione nuovo entity (non passo alcun parametro perchè l'id è autoincrement)
t = new Esempiotable();
//Memorizzazione entity all'interno del persistence Context attraverso em(EntityManager)
em.persist(t);
}
In questa porzione di codice ad ogni creazione dell'entity esso verrà inserito all'interno del persistence context, ciò ad ogni ciclo incrementa il consumo di bytes all'interno dell'heap.
Effettuando un test del precedente codice attraverso il profiler di netbeans è possibile notare come il consumo di memoria massimo durante il ciclo si aggiri intorno ai 250MB per un Entity dotato di soli due campi (Integer e String (peraltro lasciato vuoto))
Se anzichè 500000 entity ne creassimo molti di più otterremmo un Eccezione di tipo OutOfMemoryException
La Soluzione
Qual'è il "contenitore" che si riempie? Il PERSISTENCE CONTEXT!!
Quindi è necessario di tanto in tanto svuotarlo.
Esempiotable t;
for (int i = 0; i < 500000; i++)
{
//Creazione nuovo entity (non passo alcun parametro perchè l'id è autoincrement)
t = new Esempiotable();
//Memorizzazione entity all'interno del persistence Context attraverso em(EntityManager)
em.persist(t);
if ((i % 20) == 0)//Ogni venti inserimenti
{ em.flush();//rendiamo persisteni le modifiche (non necessario se em.flushModeType = COMMIT
em.clear();//ripuliamo il persistence context
}
}
Attraverso il flushing e relativo clear il consumo di memoria nella stessa situazione provata prima non supera i 48MB.
Ciò dimostra che il metodo funziona non solo teoricamente ma anche nella pratica.
nota:
Nel caso sia attiva una cache di secondo livello, essa deve essere disabilitata per le operazioni batch.
Conclusione
La tecnica "flush and clear" funziona anche nel caso di update, in tal caso se si usa hibernate si può optare per lo ScrollableResults in modo da usare i cursori per velocizzare il processo di estrazione dei dati e non intasare la memoria nel caricamento di troppi dati nel persistence context.
Inoltre come consigliato nella guida hibernate, bisogna settare nella configurazione, la seguente proprietà:
hibernate.jdbc_batch_size = 20;
in modo da abilitare il batch processing a livello jdbc.
Riferimenti.

EntityManager FLUSH

EntityManager: Comportamento del metodo Flush
G.Morreale

Introduzione:


Come ho accennato nel precedente articolo se si usa un EntityManager JTA la transazione è delimitata dall'inizio e fine di un metodo.
L'istruzione dell'EntityManager sulla quale vorrei fare chiarezza è l'operazione di flush. (metodo EntityManager.flush())

La principale funzione del flush, senza scendere nei dettagli relativi alle relazioni tra Entity, è quella di sincronizzare il persistenceContext con il database sottostante.

L'EntityManager può avere due tipologie di FlushMode

FlushModeType.AUTO(Default) - Il flush avviene non appena viene eseguita una query in automatico
FlushModeType.COMMIT - Il flush avviene solo sul commit della transazione.


Esempio

Supponiamo di avere l'entity Esempiotable dotato di due campi (id, testo)
e di eseguire il seguente codice, con opportune varianti:

        //FLUSHMODETYPE.COMMIT - Senza Flush
        em.setFlushMode(FlushModeType.COMMIT);
        Esempiotable es = em.find(Esempiotable.class, 1);
        es.setTesto("ciao");
        String nuovoTesto = es.getTesto();
        System.out.println(nuovoTesto);
        
        es = (Esempiotable) em.createQuery("SELECT Object(o) FROM Esempiotable o WHERE o.id = 1").getResultList().get(0);
        nuovoTesto = es.getTesto();        
        System.out.println(nuovoTesto);
        
 //em.flush();        LINEA COMMENTATA!!!!!
        nuovoTesto = es.getTesto();
        System.out.println(nuovoTesto);


Il codice precedente, usando come persistence provider hibernate, genera il seguente sql:

Hibernate: 
    select
        esempiotab0_.id as id48_0_,
        esempiotab0_.testo as testo48_0_ 
    from
        esempiotable esempiotab0_ 
    where
        esempiotab0_.id=?
ciao
Hibernate: 
    select
        esempiotab0_.id as id48_,
        esempiotab0_.testo as testo48_ 
    from
        esempiotable esempiotab0_ 
    where
        esempiotab0_.id=1
ciao
ciao

In sostanza disabilitando l'auto flushing e non chiamando esplicitamente il metodo flush non viene eseguita l'update che porterebbe alla modifica della riga corrispondente all'istanza dell'entity.
Nonostante ciò è possibile notare come il testo stampato sia sempre ciao perchè le modifiche anche se non riportate sul db sono ben presenti nel persistence context.

Altro caso:

          //FLUSHMODETYPE.COMMIT - Con Flush
        em.setFlushMode(FlushModeType.COMMIT);
        Esempiotable es = em.find(Esempiotable.class, 1);
        es.setTesto("ciao");
        String nuovoTesto = es.getTesto();
        System.out.println(nuovoTesto);
        
        es = (Esempiotable) em.createQuery("SELECT Object(o) FROM Esempiotable o WHERE o.id = 1").getResultList().get(0);
        nuovoTesto = es.getTesto();        
        System.out.println(nuovoTesto);
        
        em.flush();        
        nuovoTesto = es.getTesto();
        System.out.println(nuovoTesto);


Hibernate: 
    select
        esempiotab0_.id as id36_0_,
        esempiotab0_.testo as testo36_0_ 
    from
        esempiotable esempiotab0_ 
    where
        esempiotab0_.id=?
ciao
Hibernate: 
    select
        esempiotab0_.id as id36_,
        esempiotab0_.testo as testo36_ 
    from
        esempiotable esempiotab0_ 
    where
        esempiotab0_.id=1
ciao
Hibernate: 
    update
        esempiotable 
    set
        testo=? 
    where
        id=?
ciao


In questo caso l'update viene eseguita solo quando viene richiamato esplicitamente il metodo di sincronizzazione con il db (flush()).
        
         //FLUSHMODETYPE.AUTO - Con Flush
        em.setFlushMode(FlushModeType.AUTO);
        Esempiotable es = em.find(Esempiotable.class, 1);
        es.setTesto("ciao");
        String nuovoTesto = es.getTesto();
        System.out.println(nuovoTesto);
        
        es = (Esempiotable) em.createQuery("SELECT Object(o) FROM Esempiotable o WHERE o.id = 1").getResultList().get(0);
        nuovoTesto = es.getTesto();        
        System.out.println(nuovoTesto);
        
        em.flush();        
        nuovoTesto = es.getTesto();
        System.out.println(nuovoTesto);

Hibernate: 
    select
        esempiotab0_.id as id40_0_,
        esempiotab0_.testo as testo40_0_ 
    from
        esempiotable esempiotab0_ 
    where
        esempiotab0_.id=?
ciao
Hibernate: 
    update
        esempiotable 
    set
        testo=? 
    where
        id=?
Hibernate: 
    select
        esempiotab0_.id as id40_,
        esempiotab0_.testo as testo40_ 
    from
        esempiotable esempiotab0_ 
    where
        esempiotab0_.id=1
ciao
ciao

In questo caso l'update viene eseguito in automatico prima di eseguire una nuova query.
Il flush() richiamato esplicitamente in questo caso non deve provvedere alla sincronizzazione di null'altro, e quindi nello specifico esempio risulta inutile.

Conclusione

Un breve post per mostrare attraverso un esempio il comportamento del metodo flush in relazione al FlushModeType



Java EE: Persistenza e Transazioni

Java EE: Persistence Api, Transazioni ed EntityManager
G.Morreale

Introduzione:

L'obiettivo di questo articolo è spiegare come poter controllare le transazioni dell'Entity Manager confrontando una gestione JTA da una gestione Locale.

Ripasso sulle Transazioni

Un transazione è una sequenza di operazioni.

Esempio di transazione in mysql:

BEGIN;
UPDATE Tabella3 SET colonna3 = x;
SELECT colonna1 FROM Tabella;
UPDATE Tabella2 SET colonna2 = y;
COMMIT;


La transazione può terminare correttamente, in questo caso una particolare istruzione: COMMIT, indica al gestore delle transazioni o al DBMS di rendere persistenti tutte le operazioni che compongono la transazione.
Oppure la transazione può terminare NON correttamente (in anticipo), in questo caso si utilizza un istruzione di ROLLBACK con la quale l'effetto delle operazioni finora eseguite dalla transazione non viene reso persistente.
Rifacendoci all'esempio se la lettura di "colonna1" ha un valore tale da richiedere l'annullamento della prima UPDATE allora si richiede il ROLLBACK, utilizzando l'apposita istruzione.

La transazione per poter essere tale deve godere di un insieme di proprietà, particolarmente significative nei sistemi in cui possono essere lanciate diverse transazioni contemporaneamente.

L'insieme di proprietà che caratterizzano la transazione viene indicato con l'acronimo ACID

  • Atomicità: La transazione è un elemento indivisibile. Non è ammessa un esecuzione parziale della transazione.

  • Coerenza: Una transazione deve rispettare i vincoli di integrità

  • Isolamento: Se più transazioni vengono eseguite contemporaneamente, l'effetto deve prescindere dalle altre transazioni.

Riguardo L'isolamento sono previsti quattro livelli di isolamento:

      • Read Uncommitted: Una transazione può leggere i dati scritti dalle transazioni concorrenti. Ciò vuol dire che se una transazione fallisce le altre possono aver letto dei dati non validi.

      • Read Committed: Gli aggiornamenti di una transazione T1 vengono resi visibili a una transazione T2 non appena T1 effettua il consolidamento(Transazione terminata a buon fine - COMMIT)

      • Repeatable Read: Gli aggiornamenti di una transazione T1 vengono resi visibili al consolidamento, in più T2 se è partita prima del consolidamento continuerà a leggere gli stessi dati(quelli non aggiornati da T1) fino alla sua fine(intendo la fine di T2).

      • Serializable: Ha gli stessi vincoli del caso precedente, in più la lettura di un dato da parte di T1 provoca il blocco degli aggiornamenti fino al termine della transazione.

  • Durabilità: Gli effetti di una transazione terminata correttamente devono essere persistenti nel tempo.

La concorrenza della transazioni porta a diverse anomalie(per capire attraverso degli esempi clicca qui)

  • Lost Update: perdita di aggiornamenti effettuati da una transazione
  • Dirty Read:una transazione T1 legge uno stato non più effettivo a causa di un rollback da parte di T2
  • Unrepeatable Read:T1 rilegge gli stessi dati e li trova cambiati
  • Phantom Read:Per effetto di inserimenti di nuove tuple una transazione riesegue una query ottenendo risultati diversi.

Di seguito una tabella riepilogativa che mostra quali anomalie vengono risolte con i diversi tipi di isolamento:

Dirty readNon repeatable readPhantom readLost updates
Read uncommittedSISISISI
Read committed
NOSISINO
Repeatable readNONOSINO
SerializableNONONONO

Java EE e la persistenza

Dopo aver brevemente richiamato i concetti chiave relativi alle transazioni, cerchiamo di capire quali sono gli attori e gli elementi che entrano in gioco quando si parla di ejb e persistenza in ambito Java EE.

Mapping ORM

Gran parte dei progetti software richiedono l'esistenza di un dbms per la gestione dei dati.
I dbms nella maggior parte dei casi sono di tipo relazionale, quindi l'elemento chiave è la relazione(una relazione è una tabella.. ) a differenza della programmazione object oriented  dove l'elemento chiave è l'oggetto.

Per far convivere questi due modi di vedere delle componenti esistono dei framework che si occupano di creare una corrispondenza biunivoca tra una rappresentazione ad oggetti e una relazione(http://it.wikipedia.org/wiki/Object-relational_mapping).

Di seguito un elenco di alcuni di questi framework:


Grazie a questi framework è rendere portabile(o quasi!!) un componente software rispetto al dbms usato(E' possibile ad esempio passare da mysql ad oracle senza modificare il codice del progetto).


JSR 220 - Enterprise Java Bean

La specifica JSR-220 prevede la definizione delle specifiche riguardanti le varie tipologie di Java Bean.
Visto la tipologia di articolo mi soffermerò su accennare solo a parte di tale specifica, ovvero quella relativa alle Persistence API.

Si è parlato di mapping tra relazione ed oggetti.

Bene, gli oggetti sui quali si mappano le relazioni (Tabelle e righe) sono gli EntityBean.
Un EntityBean è una classe dotata di particolari annotation (Es. @Entity) (o in alternativa di opportuni descrittori xml) e altri vincoli.. in grado di rappresentare una relazione.

Chi si occupa di gestire gli EntityBean?
Gli EntityBean sono gestiti dall'EntityManager

L'EntityManager, è l'oggetto associato al persistence context.

Il persistence context è un insieme di istanze di entity identificate univocamente. All'interno del persistence context gli entity e il loro lifecycle sono managed(sotto il controllo del persistence context).

Persistence Unit:Indica quali classi sono gestite da un determinato EntityManager nonché a quale datasource fanno riferimento, è una sorta di "elemento di configurazione"

L'EntityManager, quindi, interagisce con un persistence context al fine di effettuare creazione, eliminazione, aggiornamento, ricerca e query sui vari entity del contesto, nonchè comunicare con il db al fine di permettere il mapping tra la rappresentazione ad oggetti e quella relazionale dei dati.



Transazioni, Scope etc.. Operare delle scelte

Le scelte da operare sono diverse:

Chi decide quando creare o eliminare un EntityManager? L'applicazione o il container?

Le opzioni sono due, o lo gestisce il container o lo gestisce l'applicazione generando e chiudendo con apposite istruzioni l'entityManager.
Se vogliamo che in automatico venga generato e distrutto un entityManager dal container l'unica possibilità è quella di optare per un EntityManager JTA

Container-Managed Persistence Context (JTA Entity Manager)

@Stateless
public class TestImpl implements TestLocal
{
//EntityManager ottenuto tramite dependency injection
@PersistenceContext EntityManager em;

    public Dato getDato(Long id)
    
        return em.find(Dato.class, id); //Uso l'entityManager senza occuparmi di crearlo, e distruggerlo
    }
}

Se invece decidiamo di controllare la vita dell'entityManager si può optare per un entityManager JTA oppure entityManager ResourceLocal.

Application-Managed Persistence Context (JTA Entity Manager)

@Stateless
public class TestImpl implements TestLocal
{
//EntityManager ottenuto tramite dependency injection
@PersistenceUnit EntityManagerFactory emf;

    public Dato getDato(Long id)
    
             EntityManager em = emf.createEntityManager();//Inizializzo l'entityManager
        Dato = (Dato)em.find(Dato.class, id); //Uso l'entityManager
             em.close();//Chiudo l'entityManager            
    }
}

Application-Managed Persistence Context (Resource Local Entity Manager)

@Stateless
public class TestImpl implements TestLocal
{


    public Dato getDato(Long id)
    
             EntityManagerFactory emf = Persistence.createEntityManagerFactory();
             EntityManager em = emf.createEntityManager();//Inizializzo l'entityManager
             em.getTransaction().begin();
        Dato = (Dato)em.find(Dato.class, id); //Uso l'entityManager
             em.getTransaction().commit();
             em.close();//Chiudo l'entityManager            
    }
}

In entrambi i casi viene creato(createEntityManager()) e distrutto(close()) dall'applicazione.
Però con la gestione JTA la transazione inizia e termina con l'inizio e la fine del metodo nel secondo caso invece l'inizio e la fine della transazione sono decisi dallo sviluppatore (quindi dall'applicazione)

Inoltre nel caso di transazioni JTA controlled, il rollback viene richiamato nel caso in cui si verifica un eccezione all'interno del metodo. Altra possibilità di Rollback è l'invocazione del metodo setRollbackOnly (appartenente all'interfaccia EJBContext).


Un'altra scelta da effettuare, in qualsiasi dei casi visti precedentemente, è quella relativa allo scope del persistence context.
Le opzioni sono due:

  • Transaction Scoped Persistence Context
  • Extended Scoped Persistence Context


Questa scelta influisce sulla vita di un persistence context.

Nel primo caso esso viene generato e distrutto insieme all'inizio e alla fine di una transazione sia essa gestita da un JTA EntityManager che da un ResourceLocal EntityManager.

Nel secondo caso invece, la vita del persistence context non è legata all'inizio e alla fine di una transazione, ma è legata alla vita dell'EntityManager.
Nel caso in cui l'EntityManager è gestito dal container il persistence context viene creato non appena esso è ottenuto attraverso operazione di lookup o dependency injection, nel caso di EntityManager gestito dall'applicazione esso viene creato e distrutto rispettivamente sulle istruzioni EntityManagerFactory.createEntityManager(), EntityManager.close().

Conclusione

Questo è un articolo in un certo senso "difficile" in quanto entrano in gioco diversi e diversi concetti.
Più che altro è un quadro d'insieme per chi già conosce questi concetti, spero però che sia anche uno punto di partenza per chi non li conosce ancora.

Riferimento: JSR 220 Documentation.