Container Managed Persistence Contesti
Quando si utilizza contesti di persistenza gestiti dal contenitore (come siete tramite annotazioni @PersistenceContext), la specifica JPA specifica che solo un contesto di persistenza può essere associato ad una transazione JTA.
Il contesto di persistenza viene creato dal contenitore Java EE. Nonostante le apparenze del codice (le annotazioni @PersistenceContext sembrano suggerire che il PC sia iniettato direttamente nelle variabili dell'istanza di EntityManager), il contesto di persistenza viene effettivamente memorizzato come riferimento ENTRO LA TRANSAZIONE JTA. Ogni volta che un'operazione di EntityManager si verifica, non fa riferimento al proprio contesto di persistenza interno. Invece esegue un'operazione speciale perché è gestita dal contenitore: cerca sempre il contesto di persistenza all'interno della transazione JTA e la usa. Questo è chiamato propagazione del contesto di persistenza JTA.
alcune citazioni dalla specifica JPA:
Quando si utilizza un gestore di entità gestita dal contenitore, il ciclo di vita del contesto persistenza è sempre gestito automaticamente, in modo trasparente per l'applicazione, e il contesto di persistenza viene propagato con la transazione JTA .
Container gestiti Transaction-ambito contesto di persistenza
... Un nuovo contesto di persistenza inizia quando il contenitore gestiti entità responsabile viene richiamato [76] nell'ambito di una transazione JTA attiva, e c'è nessun contesto di persistenza corrente già associato alla transazione JTA . Il contesto di persistenza viene creato e quindi associato a con la transazione JTA.
Container gestiti esteso contesto di persistenza
... un contesto di persistenza esteso gestita dal contenitore può essere avviata solo nell'ambito di un session bean stateful. Esiste dal punto in cui il bean di sessione stateful che dichiara una dipendenza da un gestore di entità di tipo PersistenceContextType.EXTENDED viene creato e si dice che sia associato al bean di sessione stateful. La dipendenza dal contesto di persistenza esteso viene dichiarata mediante l'annotazione PersistenceContext o l'elemento descrittore di deployment ref contest di persistenza. Il contesto di persistenza viene chiuso dal contenitore quando il metodo @Remove del bean di sessione stateful viene completato (oppure l'istanza di bean di sessione stateful viene altrimenti distrutta).
Requisiti per Persistence Context Propagazione
... Se un componente si chiama e non v'è nessuna transazione JTA ..., il contesto di persistenza non è propagato. • Il richiamo di un gestore di entità definito con PersistenceContext- Type.TRANSACTION comporterà l'uso di un nuovo contesto di persistenza. • Il richiamo di un gestore di entità definito con PersistenceContext- Type.EXTENDED comporterà l'utilizzo del contesto di persistenza esteso esistente associato a quel componente.
... Se un componente viene chiamato e la transazione JTA viene propagata in quel componente: • Se il componente è un bean di sessione stateful a cui è stato associato un contesto di persistenza esteso e vi è un diverso contesto di persistenza associato a la transazione JTA, un EJBException viene lanciato dal contenitore. • Altrimenti, se esiste un contesto di persistenza associato alla transazione JTA, tale contesto di persistenza viene propagato e utilizzato.
Quindi questo è il tuo problema. L'ovvia domanda da $ 64: perché la specifica lo richiede ???
Bene, è perché è un compromesso intenzionale che porta la potente magia di EntityManager agli EJB.
Utilizzando le transazioni JTA per propagare un unico contesto di persistenza ha una limitazione: le transazioni non possono cavalcare molteplici contesti di persistenza, in modo da non possono cavalcare più database.
Tuttavia, ha anche un enorme vantaggio: qualsiasi entityManager dichiarato negli EJB può condividere automaticamente lo stesso contesto di persistenza e quindi può operare sullo stesso set di entità JPA e partecipare alla stessa transazione. È possibile avere una catena di EJB che richiama altri bean di qualsiasi complessità e si comportano tutti in modo ragionevole e coerente rispetto ai dati dell'entità JPA.Inoltre, non richiedono la complessità di intensionare/condividere in modo coerente i riferimenti del gestore di entità attraverso invocazioni di metodi: gli EntityManager possono essere dichiarati privatamente in ciascun metodo. La logica di implementazione può essere molto semplice.
La risposta al vostro problema: utilizzare Application-Managed Persistence contesti (via EntityManagers applicativi gestiti)
dichiarare il vostro entityManager tramite uno di questi approcci:
// "Java EE style" declaration of EM
@PersistenceUnit(unitName="H2PU")
EntityManagerFactory emfH2;
EntityManager emH2 = emfH2.createEntityManager();
O
// "JSE style" declaration of EM
EntityManagerFactory emfH2 = javax.persistence.Persistence.createEntityManagerFactory("H2PU");
EntityManager emH2 = emfH2.createEntityManager();
and the same for emfOracle & emOracle.
È necessario chiamare em.close() al termine di ogni EM, preferibilmente tramite un finale { } clausola o tramite un'istruzione try-with-resources di Java 7.
Gli EM gestiti dall'applicazione continuano a partecipare (cioè a sincronizzare con) le transazioni JTA. Qualsiasi numero di EM gestiti da applicazioni può partecipare a una singola transazione JTA, ma nessuno di questi avrà mai il proprio contesto di persistenza associato o propagato a qualsiasi contenitore gestito EM.
Se l'EntityManager viene creata al di fuori del contesto di una transazione JTA (prima che la transazione è iniziata), allora si deve chiedere che esplicitamente di aderire alla transazione JTA:
// must be run from within Java EE code scope that already has a JTA
// transaction active:
em.joinTransaction();
O ancora più semplice, se l'EntityManager è creato all'interno del contesto di una transazione JTA, quindi EntityManager gestito dall'applicazione si unisce automaticamente all'implicazione della transazione JTA - non è necessario joinTransaction().
Quindi gli EM gestiti dall'applicazione possono disporre di una transazione JTA che si trova a cavallo di più database. Naturalmente, si può sempre puntuale esegue un locale risorsa JDBC transazione indipendente JTA:
EntityTransaction tx = em.getTransaction();
tx.begin();
// ....
tx.commit();
EDIT: dettagli in più per Transaction Management con Entity Manager
ATTENZIONE applicativi gestiti: i campioni il codice qui sotto sono per uso educativo - Li ho digitati in cima alla mia testa per aiutare a spiegare i miei punti & non hanno avuto il tempo di compilare/debug/test.
Il parametro predefinito @TransactionManagement per EJB è TransactionManagement.CONTAINER e il parametro predefinito @TransactionAttribute per i metodi EJB è TransactionAttribute.REQUIRED.
ci sono quattro permutazioni per la gestione delle transazioni:
A) EJB con Container Managed transazioni JTA
Questo è l'approccio preferito Java EE.
Classe EJB @TransactionGestione gestione:
deve essere impostato su TransactionManagement.CONTAINER in modo esplicito o omesso per utilizzare implicitamente il valore predefinito.
Metodo EJB @TransactionAttribute annotazione: deve essere impostato su TransactionAttribute.REQUIRED in modo esplicito oppure ometterlo a implicito utilizzare il valore predefinito. (Nota: se avessi uno scenario di business diverso, potresti utilizzare TransactionAttribute.MANDATORY o TransactionAttribute.REQUIRES_NEW se la loro semantica corrisponde alle tue esigenze.)
Gestori entità gestite dall'applicazione:
devono essere creati tramite Persistence.createEntityManagerFactory ("unitName") ed emf.createEntityManager(), come descritto sopra.
Unire gli EntityManager con la transazione JTA:
Creare gli EntityManagers ENTRO un metodo EJB transazionale e si uniranno automaticamente alla transazione JTA. OPPURE se EntityManagers viene creato in precedenza, chiamare em.joinTransaction() all'interno di un metodo EJB della transazione.
EntityManager.close call() quando sono finiti li utilizzano. Questo dovrebbe essere tutto ciò che è richiesto.
esempi di base - basta usare più EntityManagers per la transazione su più DB:
@Stateless
public class EmployeeServiceBean implements EmployeeService {
// Transactional method
public void createEmployee() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("EmployeeService");
EntityManager em = emf.createEntityManager();
Employee emp = ...; // set some data
// No need for manual join - em created in active tx context, automatic join:
// em.joinTransaction();
em.persist(emp);
// other data & em operations ...
// call other EJBs to partake in same transaction ...
em.close(); // Note: em can be closed before JTA tx committed.
// Persistence Context will still exist & be propagated
// within JTA tx. Another EM instance could be declared and it
// would propagate & associate the persistence context to it.
// Some time later when tx is committed [at end of this
// method], Data will still be flushed and committed and
// Persistence Context removed .
emf.close();
}
}
@Stateful
public class EmployeeServiceBean implements EmployeeService {
// Because bean is stateful, can store as instance vars and use in multiple methods
private EntityManagerFactory emf;
private EntityManager em;
@PostConstruct // automatically called when EJB constructed and session starts
public void init() {
emf = Persistence.createEntityManagerFactory("EmployeeService");
em = emf.createEntityManager();
}
// Transactional method
public void createEmployee() {
Employee emp = ...; // set some data
em.joinTransaction(); // em created before JTA tx - manual join
em.persist(emp);
}
// Transactional method
public void updateEmployee() {
Employee emp = em.find(...); // load the employee
// don't do join if both methods called in same session - can only call once:
// em.joinTransaction(); // em created before JTA tx - manual join
emp.set(...); // change some data
// no persist call - automatically flushed with commit
}
@Remove // automatically called when EJB session ends
public void cleanup() {
em.close();
emf.close();
}
// ...
}
B) EJB con bean gestito transazioni JTA
Usa @ TransactionManagement.BEAN.
Iniettare l'interfaccia UserTransaction JTA, quindi il fagiolo può contrassegnare direttamente le transazioni JTA.
marchio manualmente/sincronizzare la transazione tramite UserTransaction.begin()/commit()/rollback().
Verificare l'EntityManager unisce la transazione JTA - sia creare la EM in un contesto di transazione JTA attiva o chiamare il numero em.joinTransaction().
Esempi:
@TransactionManagement(TransactionManagement.BEAN)
@Stateless
public class EmployeeServiceBean implements EmployeeService {
// inject the JTA transaction interface
@Resource UserTransaction jtaTx;
public void createEmployee() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("EmployeeService");
EntityManager em = emf.createEntityManager();
try {
jtaTx.begin();
try {
em.joinTransaction();
Employee emp = ...; // set some data
em.persist(emp);
// other data & em operations ...
// call other EJBs to partake in same transaction ...
} finally {
jtaTx.commit();
}
} catch (Exception e) {
// handle exceptions from UserTransaction methods
// ...
}
Employee emp = ...; // set some data
// No need for manual join - em created in active tx context, automatic join:
// em.joinTransaction();
em.persist(emp);
em.close(); // Note: em can be closed before JTA tx committed.
// Persistence Context will still exist inside JTA tx.
// Data will still be flushed and committed and Persistence
// Context removed some time later when tx is committed.
emf.close();
}
}
C) POJO/non-EJB con le transazioni (bean gestito) codificati a mano delle risorse locali (non JTA)
È sufficiente utilizzare l'interfaccia JPA EntityTransaction per TX demarcazione (ottenuto tramite em.getTransaction()).
Esempio:
public class ProjectServlet extends HttpServlet {
@EJB ProjectService bean;
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// ...
try {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPU");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
bean.assignEmployeeToProject(projectId, empId);
bean.updateProjectStatistics();
} finally {
tx.commit();
}
} catch (Exception e) {
// handle exceptions from EntityTransaction methods
// ...
}
// ...
}
}
D) POJO/non-bean con() transazioni JTA POJO gestiti codificati a mano
Questo presuppone che il POJO/componente è in esecuzione in qualche contenitore che ha JTA supporto.
Se in un container Java EE, è possibile utilizzare Java EE risorsa iniezione di interfaccia UserTransaction JTA.
(. In alternativa, può cercare in modo esplicito una maniglia per l'interfaccia JTA e fare la demarcazione su di esso, quindi chiamare em.getTransaction() JoinTransaction() - vedi JTA spec.)
Esempio:
public class ProjectServlet extends HttpServlet {
@Resource UserTransaction tx;
@EJB ProjectService bean;
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// ...
try {
tx.begin();
try {
bean.assignEmployeeToProject(projectId, empId);
bean.updateProjectStatistics();
EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPU");
EntityManager em = emf.createEntityManager();
// Should be able to avoid explicit call to join transaction.
// Should automatically join because EM created in active tx context.
// em.joinTransaction();
// em operations on data here
em.close();
emf.close();
} finally {
tx.commit();
}
} catch (Exception e) {
// handle exceptions from UserTransaction methods
// ...
}
// ...
}
}
è solo un errore di copia-incolla, o avete il nome dell'unità di sbagliato nel vostro '@ PercistenceContext'? Dovrebbe essere 'FenixRadarPU'? – Magnilex
E 'un errore di copia incolla. L'ho già corretto Grazie! –
Aggiungere il '@PersistenceContext (unitName = "...")' annotazioni direttamente ai 'EntityManager's nella classe' Service' per cercare di determinare se questo è un problema CDI, o un problema di JPA. –