Nel post precedente abbiamo affrontato EJB, JPA e JTA nel loro utilizzo più frequente (e più semplice). Vediamo adesso di capire cosa possiamo fare quando rinunciamo all’EntityManager a favore dell’EntityManagerFactory. Perché scendere di un livello e avere più controllo? In un contesto Java EE magari non è molto utile perché complica il codice, ma è bene sapere cosa si può fare a partire da questa factory.

La fabbrica di EntityManager

In un contesto Java SE, per lavorare con JPA bisogna necessiamente passare dall’EntityManagerFactory (a meno che non si usi Spring…), e comunque il tipo di DataSource sarà quasi sicuramente di tipo Resource Local (RL). In Java EE invece le combinazioni possono essere più disparate…
Prima cosa da ricordare:
una volta creato un EntityManager dalla factory, ricordiamoci di chiuderlo per liberare le risorse.

Seconda cosa: rinfreschiamo gli acronimi introdotti nel post precedente.

  • CMT: Container Managed Transaction
  • BMT: Bean Managed Transaction
  • Datasource JTA: Datasource gestito da Java Transaction API
  • Datasource RIL: Datasource di tipo “Resource Local”

Le mille varianti

Riprendiamo quindi il contesto Java EE: prendiamo un EJB stateless, teniamo fisso l’EntityManagerFactory e proviamo a variare:

  • tipo di gestione delle transazioni (CMT o BMT)
  • tipo di datasource (JTA o RL)

Vediamo cosa succede!

CMT e JTA

Lasciamo quindi gestire le transazioni al container: il DataSource può essere tranquillamente JTA:
[java]
@PersistenceUnit
private EntityManagerFactory emf;
public void save(Person person) {
EntityManager em = emf.createEntityManager();
try {
em.persist(person);
} finally {
em.close();
}
}
[/java]
Per ottenere la factory basta usare l’annotazione @PersistenceUnit (occhio che per l’EntityManager invece era @PersistenceContext!). Il codice è molto semplice: viene creato l’EntityManager e nella clausola finally viene chiuso, mentre la transazione per eseguire il il salvataggio della persona è gestita dal Container.

CMT e RL

Il titolo in questo caso è barrato perché sostanzialmente non si può fare: non possiamo chiedere al container di gestire transazioni su datasource Resource Local perché non sono di sua competenza: dovranno essere gestite per forza manualmente. Il codice dell’esempio precedente quindi se eseguito su un datasource RL non darà errore, ma non verrà eseguito nessun commit!

BMT e RL

Quello che possiamo fare quindi in questo momento è gestire la transazione: essendo fuori JTA, non possiamo usare l’interfaccia UserTransaction, ma in questo caso abbiamo a disposizione l’interfaccia EntityTransaction.
[java highlight=”10″]
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class PersonEmfBmtRlService {
@PersistenceUnit
private EntityManagerFactory emf;
public void save(Person person) {
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
try {
transaction.begin();
em.persist(person);
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
if (transaction.isActive()) {
transaction.rollback();
}
throw e;
} finally {
em.close();
}
}
}
[/java]
Avevate già notato il metodo EntityManager#getTransaction()? Non era permesso chiamarlo in contesti JTA, ma adesso possiamo (anche perché è l’unico modo di gestire le transazioni!!). Il codice perde molta dell’eleganza vista negli esempi precedenti (diventa un po’ procedurale – crea entity manager, apri transazione, persisti, committa), concetti che stanno molto bene relegati ad un contesto AOP, ed è proprio quello che fa normalmente EJB in un contesto CMT.
In due parole quindi:

  • UserTransaction: gestisce transazioni in contesti JTA. Si inietta direttamente nell’EJB tramite @Resource.
  • EntityTransaction: gestisce transazioni fuori da contesti JTA. Si ottiene dal’EntityManager.

BMT e JTA

Gli ultimi due esempi forse sono un po’ estremi, non così frequenti, almeno in un contesto EE.
Più facile invece è potersi imbattere in un caso in cui il DataSource sia JTA ma la transazione vada gestita manualmente. Il codice è semplice, ma fate molta attenzione all’ordine con cui prima viene aperta la transazione e poi creato l’EntityManager:
[java highlight=”14,15″]
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class PersonEmfBmtJtaService {
@PersistenceUnit
private EntityManagerFactory emf;
@Resource
private UserTransaction transaction;
public void save(Person person) {
EntityManager em = null;
try {
transaction.begin();
em = emf.createEntityManager();
em.persist(person);
transaction.commit();
} catch (Exception e) {
try {
e.printStackTrace();
transaction.rollback();
throw new RuntimeException(e);
} catch (IllegalStateException | SecurityException
| SystemException e1) {
e1.printStackTrace();
}
} finally {
em.close();
}
}
[/java]
Se invertiamo le due righe evidenziate, ovvero creiamo l’EntityManager prima dell’apertura della transazione, in pratica l’EntityManager non si accorge che c’è una transazione attiva a meno che non venga chiamato esplicitamente il metodo l’EntityManager#joinTransaction() (non proprio comodo…).

Riassumendo

Cerchiamo di tirare le fila di tutte le permutazioni fatte in queste prove con la seguente tabella:

CMT BMT
JTA EntityManager
EntityManagerFactory
EntityManager + UserTransaction
EntityManagerFactory + UserTransaction
RL EntityManagerFactory + EntityTransaction

In questo modo, le chiacchiere fatte fino adesso assumono una connotazione più chiara: a partire da questa tabella, la prossima volta che avrò a che fare con EntityManagerFactory, EntityManager, UserTransaction o EntityManagerFactory saprò come incastrarli!