Visualizzazione post con etichetta JPA. Mostra tutti i post
Visualizzazione post con etichetta JPA. Mostra tutti i post

domenica 23 settembre 2012

Jpa mappare Primary Key

La primary key è l'identità di un entity bean.
L'annotazione  @javax.persistence.Id identifica la proprietà che funge da primarìy key.
La chiave primaria può essere autogenerata dal provider, in questo caso si utilizza l'annotazione
@javax.persistence.GeneratedValue con le seguenti strategie (attributo strategy):
  • TABLE;
  • SEQUENCE;
  • IDENTITY;
  • AUTO.
AUTO

E'  la strategia di default  che utilizza le colonne autoincrementali presenti in molti database come MySql o Sql Server.

IDENTITY

Con questa strategia si obbliga il provider ad utilizzare i campi autoincrementali del db (esempio in mysql AUTO_INCREMENT e in Sql Server IDENTITY ) Per adottare questa strategia basta fare così:
....
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
public long getId(){
.....
}

TABLE

Con questa strategia si definisce una tabella utilizzata come "serbatoio" per fornire le chiavi primarie.
La struttura di questa tabella è la seguente:

create table GENERATOR_TABLE
{
     PRIMARY_KEY_COLUMN VARCHAR not null,
     VALUE_COLUMN long not null
}

Bisogna utilizzare una altra annotazione @TableGenerator dove si specifica il generatore con il nome della tabella creata etc etc.

Esempio

@TableGenerator(name="myGenerator",table="GENERATOR_TABLE",pkColumnName="PRIMARY_KEY_COLUMN",
valueColumnName="VALUE_COLUMN",pkColumnValue="CUST_ID",allocationsize=10)
@Id
@GeneratedValue(strategy=GenerationType.TABLE,generator="myGenerator")
public long getId(){...}

SEQUENCE

Alcuni RDBMS come Oracle hanno un meccanismo built in per la generazione delle primary key, definito appunto SEQUENCE.
In questi casi a livello di classe va definito il sequence generator utilizzato e referenziato dall'attributo generator dell'annotazione @GeneratedValue.
Esempio:

@Entity
@Table(name="CUSTOMER_TABLE")
@SequenceGenerator(name="CUSTOMER_SEQUENCE",sequenceName="CUST_SEQ")
public class Customer implements Serializable{

.....
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="CUSTOMER_SEQUENCE")
public long getId(){...}
....
}

lunedì 6 agosto 2012

Ottenere IntialContext da Jboss e WAS

Memo su come ottenere InitialContext

JBOSS

....

import javax.naming.InitialContext;
import javax.naming.Context;
import javax.naming.NamingContext;
public static Context getInitialContext() throws javax.naming.NamingException
{

Properties p=new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");        
p.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
p.put(Context.PROVIDER_URL, "jnp://127.0.0.1:1099");      
 retutn new InitialContext(p);
}

.....


WAS

....

import javax.naming.InitialContext;
import javax.naming.Context;
import javax.naming.NamingContext;
public static Context getInitialContext() throws javax.naming.NamingException
{

Properties p=new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "com.ibm.ejs.ns.jndi.CNInitialContextFactory");       
p.put(Context.PROVIDER_URL, "iiop:///");      
 retutn new InitialContext(p);
}

.....

lunedì 9 aprile 2012

JPA Query con clausola ORDER BY

C'è una differenza fondamentale nell'eseguire una query sql con clausola order by rispetto ad una query JPA.
Data la seguente tabella SQL:

CREATE TABLE `BIDS` (
  `bid_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `bidder_id` varchar(45) DEFAULT NULL,
  `item_id` int(10) unsigned DEFAULT NULL,
  `bid_price` double DEFAULT NULL,
  `bid_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`bid_id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1


E' perfettamente lecito in sql scrivere la seguente query:


select b.bid_id from BIDS b  order by b.bid_price desc;

Quindi avere nell'order  by un campo non incluso nella clausola select.

In una query JPA ciò NON E' POSSIBILE in quanto i campi presenti nell'order by devono essere parte della proiezione in select.
Quindi, ipotizzando che la tabella sia mappata come oggetto con gli stessi nomi in sql
scrivere la stessa query precedentemente riportata genererà un errore.

sabato 7 aprile 2012

@MappedSuperClass

Usando questa annotation è possibile definire una classe senza associarla direttamente ad una tabella.
Quindi le entità JPA potranno poi estendere tale classe e in questo caso si effettueranno le mappature dei campi della classe annotata con @MappedSuperClass.
Un esempio chiaro di utilizzo si trova sul javadoc della JEE 5 (http://docs.oracle.com/javaee/5/api/javax/persistence/MappedSuperclass.html)

domenica 11 marzo 2012

JPA FETCH JOIN

Nelle query con JPA si utilizza questa clausola per forzare l' EAGER LOADING delle Entity associate nella query.
Ad esempio, supponiamo di avere questa situazione:

@Entity
public class Presentation
{
........
@ManyToOne
private Student presenter;
......

}


@Entity
public class Student
{
 ......
private int score;
@OneToMany(cascade=CascadeType.ALL,fetch=LAZY,mappedBy="presenter")
private Collection <Presentation> presentations;

}

Se in una sola query vogliamo tirare fuori tutti gli studenti assieme alle loro presentazioni allora scriveremo:

SELECT s from Student s left join fetch s.presentations;

domenica 4 marzo 2012

EntityManager merge vs flush

La differenza fondamentale tra l'usare il metodo merge e il persist dell' EntityManager è che il metodo persist lascia l'entità in uno stato "managed" anche dopo la sua invocazione, mentre il metodo merge no.
Si può verificare il comportamento con un semplice esempio, supponiamo che il nostro Ejb abbia i 2 seguenti metodi:

.......
public void inserisci(Persona p) {
        System.out.println("Inserisco la persona");
        em.persist(p);
        p.setEta(99);

    }

public void inserisciM(Persona p) {
        System.out.println("Inserisco la persona");
        em.merge(p);
        p.setEta(99);
    }
.....


Il codice client che richiama i metodi è il seguente:

public static void main(String[] args) throws Exception {
        Properties p=new Properties();
        p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
        p.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
        p.put(Context.PROVIDER_URL, "jnp://127.0.0.1:1099");
        Context ctx=new InitialContext(p);
        GestoreDao gd=(GestoreDao)ctx.lookup("Anagrafe/remote");
        System.out.println("Ok alla lookup");
        Persona p1=new Persona();
        p1.setCognome("Rossi");
        p1.setNome("marco");
        p1.setEta(32);
        gd.inserisci(p1);
        Persona p2=new Persona();
        p2.setCognome("Verdi");
        p2.setNome("Gino");
        p2.setEta(32);
        gd.inserisciM(p2);
    }


Nel primo caso, chiamando il metodo persist, il valore dell'età della persona Marco Rossi sarà settata a 99, nel secondo caso, chiamando il metodo merge invece resterà al valore base di 32.

mercoledì 25 gennaio 2012

File orm.xml

Sebbene con JPA si preferisce utilizzare le annotation a livello di classe è sempre possibile mappare via xml le relazioni oggetti-tabelle, nel file orm.xml.
Tale file ove presente ha la priorità sulle annotation, è consentito quindi modificare al volo alcuni dettagli (es cambio di nome colonna su db) senza per questo ricompilare le classi dopo aver modificato le annotations.
Il file va messo nella directory META-INF del nostro jar, assieme al persistence.xml.
E' possibile inoltre scrivere un proprio file di mapping, denominato ad esempio myMapping.xml ed includerlo nell'elemento mapping-file del persistence.xml, questo non esclude comunque che ove presente sia letto il file orm.xml, il file di mapping è diciamo una aggiunta.
Vediamo un esempio di file persistence.xml modificato per introdurre tale parametro:

...

<persistence-unit name="test">
     <description>Persinstence Unit</description>
     <provider>com.objectdb.jpa.Provider</provider>
     <mapping-file>META-INF/mappingFile.xml</mapping-file>
     <jar-file>packedEntity.jar</jar-file>
     <class>it.MyEntity1</class>
     <class>it.MyEntity2</class>
     <properties>
       <property name="javax.persistence.jdbc.url"
                 value="objectdb://localhost/my.odb"/>
       <property name="javax.persistence.jdbc.user" value="admin"/>
       <property name="javax.persistence.jdbc.password" value="admin"/>
     </properties>
   </persistence-unit>



lunedì 23 gennaio 2012

Jpa annotation su properties oppure fields

Secondo le specifiche EJB 3.0 è possibile avere le annotation sui campi delle Entity class oppure sulle property che fungono da accessor per tali campi.
E' importante notare che il consiglio dato dalla specifica è utilizzare nella stessa entità o le annotation sui fields oppure sulle properties, mixando le due cose si possono avere dei problemi e non è garantito il funzionamento su tutte le implementazioni di JPA.
La spiegazione su quale approccio sia meglio usare è oggetto di varie discussione.
Qui si può trovare un post interessante in merito.

domenica 22 gennaio 2012

Come ottenere un'istanza di un EntityManager

L'interfaccia javax.persistence.EntityManager, che consente di operare modifiche sul database ed è definito sul testo "EJB in ACTION" "the bridge between the OO and relational word", può essere ottenuta in due modi:
  • Tramite l'annotation @PersistenceContext come variabile di classe, ed in tal caso si parla di "container manager EntityManager";
  • Tramite l'interfaccia javax.persistence.EntityManagerFactory, ed in questo caso si parla di "application manager EntityManager".
Nel primo caso tutto è gestito dal container. Bisogna avere l'accortezza di evitare di utilizzare questo tipo di injection direttamente come variabile di istanza di una servlet, non essendo quest'ultima thread safe per definizione (a meno di implementare l'interfaccia SingleThreadModel).

Nel secondo caso bisogna scrivere il codice per controllare ogni aspetto del ciclo di vita dell' EntityManager, inizializzandolo nel @PostConstruct e chiudendolo nel @PreDestroy, mentre per ottenere l'EntityManagerFactory si utilizza l'annotation @PersistenceUnit

Di seguito un esempio di utilizzo:

@Stateless
public class ItemManagerBean implements ItemManager {

@PersistenceUnit
private EntityManagerFactory emF;
private EntityManager entityManager;
public EntityManager(){
}
@PostConstruct
public void initialize(){
entityManager=emF.createEntityManager();
}
..... metodi di business in cui si utilizza l'EntityManager

@PreDestroy
public void cleanUp(){
if(entityManager.isOpen()){
entytyManager.close();
}
}

}

sabato 21 gennaio 2012

Optimistic Locking

Quando si realizzano applicazioni con un altro grado di concorrenzialità bisogna curare particolarmente gli aspetti relativi ai lock sul db.
In un sistema concorrenziale sono possibili 3 tipi di problemi:
  • Dirty Read : Un utente può leggere dati modificati e non ancora committati da altre transazioni, dati che quindi non è detto che debbano poi essere persistiti.
  • Nonrepeatable read : L'utente può leggere e modificare i dati non ancora committati, ad esempio l'entità USER prende l'entità ITEM, che è in gestione da parte di una transazione X che alla fine cancella ITEM. A questo punto USER ha ancora la copia dell'entità ITEM originale e può modificarla ma chiaramente andando a persistere il tutto su DB si incapperà in errori apparentemente inspiegabili;
  • Phantom read: accade quando l'utente nel corso della stessa transazione va a leggere per due volte un'entità e trova che la seconda volta i valori sono cambiati rispetto alla prima, poichè nel frattemo un'altra transazione ha modificato gli stessi
I livelli di isolamento tipicamente gestiti dai moderni DBMS sono i seguenti (ordinati dal piu basso livello di isolamento al più alto):

  • READ UNCOMMITTED: La transazione può leggere dati non committati di altre transazioni. Questo livello di isolamento porta inevitabilmente al rischio di DIRTY READ;
  • READ COMMITTED: La transazione può leggere soltanto dati committati, questo è il livello base per molti DBMS;
  • REPEATABLE READ: Con questo livello di isolamento si garantisce che nel corso della transazione qualora si acceda più volte allo stesso oggetto i valori restituiti saranno sempre gli stessi, per evitare il problema del Phantom Read;
  • SERIALIZABLE: E' il livello più alto di isolamento e garantisce che nessuna delle tabelle interessate dalla transazione sarà modificata da altri utenti. In questo modo chiaramente le performance dell'applicazione ne risentono molto.
Le strategie utilizzate per affrontare i problemi di concorrenzialità con gli EJB e JPA sono di 2 tipi:
  • Pessimistic Locking;
  • Optimistic Locking.
Il Pessimistic Locking parte dal presupposto che bisogna assolutamente impedire che si verifichino problemi di concorrenzialità per cui si lockano tutte le tabelle coinvolte nel database. JPA non provvede supporto per questa strategia, sebbene alcuni persistence provider abbiano estensioni proprietarie per gestirla.

L'Optimistic Locking invece è una strategia che parte dal presupposto che i problemi di concorrenzialità siano minimi e che sia quindi meglio risolverli quando accadono piuttosto che ingessare troppo il sistema.

L'implementazione dell'optimistic locking si basa sul fatto che quando un utente ottiene un oggetto dal db in realtà lavora su una copia dello stesso e poi al momento della persistenza dell'oggetto sul db il sistema rileva se nel frattempo quell'oggetto è stato modificato ed in tal caso torna un errore da gestire.
Per realizzare questa strategia si aggiungono alle tabelle interessate delle colonne di tipo version che possono essere interi o timestamp.

Vediamo un esempio:

@Entity
@Table("PERSONA")
public class Persona implements Serializable
@Id
@Column(name="ID_PERSONA")
protected long idPersona;
.......
@Version
@Column(name="VERSION")
private Long version;

......

Il campo version non può essere gestito dall'applicazione ma è modificato soltanto dal persistence provider.
Se all'atto dell'insert/update/delete dell'entità il sistema verifica un diverso valore del campo version torna all'utente una eccezione di tipo
javax.persistence.OptimisticLockException, una RuntimeException.

E' possibile con JPA anche gestire manualmente il lock, in questo modo:

entityManager.lock(object,LockModeType.READ)

Se si prova a chiamare il lock su un oggetto senza un campo annotato come Version il sistema torna una javax.persistence.PersistenceException (runtime Exception ed eccezione padre della OptimisticLockException).
LockModeType è un Enum che può assumere valori:
  • READ per ottenere un lock in lettura;
  • WRITE per impedire a chiunque altro di agire sull'entità, qualora altre transazioni accedessero otterrebbero una OptimisticLockException.


venerdì 20 gennaio 2012

Impedance mismatch

Con il termine "impedance mismatch" si intendono le differenze di adattamento di un modello ad oggetti con il classico sistema relazionale di archiviazione dati.
Concetti come l'ereditarietà ad esempio, basilari nella programmazione ad oggetti, non sono presenti in un modello relazionale.
Con JPA sono possibili 3 strategie di "inheritance mapping", le descriviamo prendendo come un esempio la seguente gerarchia di classi volutamente semplificata:

public class Persona{
 public String nome;
public String cognome;
}


public class Studente extends Persona{
 public String matricola;
public int annoCorso;
}

public class Professore extends Persona{
public String materiaInsegnata;
}
  1. SINGLE TABLE (strategia di default per gli EJB 3.0)
  2. JOINED TABLES
  3. TABLE PER CLASS
SINGLE TABLE

Con questa strategia tutte le classi della scala gerarchica sono mappate ad un'unica tabella, che conterrà quindi l'insieme di tutti i dati e che chiameremo PERSONE.
Gli oggetti differenti sono individuati tramite una colonna di discriminazione, ad esempio nella nostra tabella potremo definirla TIPO_PERSONA char(1) e mapparla con il valore S se  Studente e P se professore.
Il modello di entità coinvolte sarà quindi di questo tipo:

@Entity
@Table(name="PERSONE")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="TIPO_PERSONA",discriminatorType=DiscriminatorType.STRING,lenght=1)
public abstract class Persona{
.....
}


@Entity
@DiscriminatorValue(value="S")
public class Studente extends Persona{
....
}

@Entity
@DiscriminatorValue(value="P")
public class Professore extends Persona{
....
}

Si noti che la strategia viene mappata nella classe radice dell'albero gerarchico.
Il grosso svantaggio di questo approccio è il mancato sfruttamento delle potenzialità del database.


JOINED-TABLES

Con questo modello si sfruttano le potenzialità del dbms, inserendo i campi comuni in una tabella e legando le tabelle professore e studente con una relazione uno a uno.
Avremo quindi una struttura di questo tipo

Tabella Persona

ID_PERSONA(PK)
NOME
COGNOME
TIPO_PERSONA

Tabella Studente
ID_PERSONA(PK)
...(campi specifici)

Tabella Professore
ID_PERSONA(PK)
...(campi specifici)


Anche in questo caso è necessario avere il campo discriminatore, la struttura ad oggetti sarà la seguente (in grassetto le differenze rispetto al precedente esempio)



@Entity
@Table(name="PERSONE")
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="TIPO_PERSONA",discriminatorType=DiscriminatorType.STRING,lenght=1)
public abstract class Persona{
.....
}


@Entity
@DiscriminatorValue(value="S")
@PrimaryKeyJoinColumn(name="ID_PERSONA")
public class Studente extends Persona{
....
}


@Entity

@DiscriminatorValue(value="P")
@PrimaryKeyJoinColumn(name="ID_PERSONA") 

public class Professore extends Persona{

....

}

Da un punto di vista del design questa scelta è sicuramente più elegante e generalmente consigliata.


TABLE-PER-CLASS

Con questa strategia sia la superclasse sia le sottoclassi hanno le loro tabelle di riferimento e non esistono relazioni tra le tabelle stesse.
In pratica le relazioni esistono solo sul modello ad oggetti mentre le tre tabelle lato DMBS non sono correlate. E' tuttavia molto più complicato eseguire query sugli oggetti e spesso bisogna ricorrere alle UNION, non molto efficienti.
Lato Java la situazione è la seguente:


@Entity

@Table(name="PERSONE")

@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)

public  class Persona{

.....

}





@Entity
 @Table(name="STUDENTE")

public class Studente extends Persona{

....

}



@Entity

@Table(name="PROFESSORE")

public class Professore extends Persona{

....

}

Questa strategia è la più confusionaria delle 3 e proprio per questo motivo per gli AS non è obbligatorio implementarla