Container Managed Persistence kontekstach
Podczas korzystania kontekstów utrwalania kontenerów zarządzane (jak jesteś poprzez adnotacje @PersistenceContext), spec JPA określa, że tylko jeden kontekst wytrwałość może być związana z transakcją JTA.
Kontekst utrwalania jest tworzony przez kontener Java EE. Pomimo pojawienia się kodu (adnotacje @PersistenceContext wydają się sugerować, że komputer jest wstrzykiwany bezpośrednio do zmiennych instancji EntityManager), kontekst utrwalania jest faktycznie przechowywany jako odniesienie W RAMACH TRANSAKCJI JTA. Za każdym razem, gdy pojawia się operacja EntityManager, nie odnosi się ona do własnego wewnętrznego kontekstu utrwalania. Zamiast tego wykonuje operację specjalną, ponieważ jest zarządzana przez kontener - zawsze wyszukuje kontekst trwałości w ramach transakcji JTA i używa go. Nazywa się to propagacją kontekstu utrwalania JTA.
kilka cytatów z spec WZP:
Kiedy używany jest menedżer jednostka pojemnik zarządzane, cykl życia kontekście trwałości zawsze jest zarządzany automatycznie, transparentnie do aplikacji i kontekstu utrwalania jest propagowany za pomocą transakcji JTA .
Pojemnik zarządzane transakcja-scoped Persistence Context
... Nowy kontekst Trwałość zaczyna się, gdy menedżer jednostka pojemnik zarządzane jest wywoływany [76] w zakresie aktywnej transakcji JTA i istnieje brak bieżącego kontekstu utrwalania już powiązanego z transakcją JTA . Kontekst utrwalania jest tworzony, a następnie powiązany z transakcją JTA .
Pojemnik zarządzane szerszym kontekście Trwałość
... Pojemnik zarządzane szerszym kontekście wytrwałość może zostać zainicjowany wyłącznie w zakresie z fasoli sesji stanowej. Istnieje od momentu, w którym stanowy komponent bean sesji , który deklaruje zależność od menedżera encji typu PersistenceContextType.EXTENDED , jest tworzony i jest przypisywany do stanowego komponentu bean sesji. Zależność od rozszerzonego kontekstu utrwalania deklarowana jest za pomocą adnotacji PersistenceContext lub elementu deskryptora wdrażania persist-context-ref. Kontekst utrwalania jest zamykany przez kontener po ukończeniu metody @Remove dla stanowego komponentu bean sesji (lub gdy w innym przypadku stanowa instancja komponentu bean sesji zostanie zniszczona).
Wymagania dotyczące Persistence Context Krzewienia
... Jeśli składnik nazywa i nie ma żadnej transakcji JTA ... kontekst wytrwałość nie jest propagowana. • Wywołanie menedżera encji zdefiniowanego przy użyciu PersistenceContext- Type.TRANSACTION spowoduje użycie nowego kontekstu utrwalania. • Wywołanie menedżera encji zdefiniowanego przy użyciu PersistenceContext- Type.EXTENDED spowoduje użycie istniejącego rozszerzonego kontekstu utrwalania związanego z tym komponentem.
... Jeśli komponent jest wywoływany, a transakcja JTA jest propagowana do tego komponentu: • Jeśli komponent jest stanowym komponentem sesji, do którego został powiązany rozszerzony kontekst utrwalania i istnieje inny kontekst utrwalania związany z transakcja JTA, wyjątek EJBException jest generowany przez kontener. • W przeciwnym razie, jeśli istnieje kontekst utrwalania związany z transakcją JTA, ten kontekst utrwalania jest propagowany i używany.
To jest twój problem. Oczywiste 64 pytanie: DLACZEGO specyfikacja wymaga tego?
Cóż, to dlatego, że jest to celowy kompromis, który przynosi potężną magię EntityManager EJB.
Stosowanie transakcji JTA do propagowania pojedynczego kontekstu utrwalania ma pewne ograniczenia: transakcje nie mogą obejmować wielu kontekstów utrwalania, więc nie mogą przechodzić przez wiele baz danych. Ta cecha ma także olbrzymią zaletę: dowolny podmiotNanager zadeklarowany w komponentach EJB może automatycznie współużytkować ten sam kontekst utrwalania i tym samym może operować na tym samym zbiorze jednostek JPA i uczestniczyć w tej samej transakcji. Możesz mieć łańcuch EJB wywołując inne EJB o dowolnej złożoności i wszystkie zachowują się rozsądnie i konsekwentnie w stosunku do danych jednostki JPA.Nie wymagają one również złożoności stałych odwołań do menedżerów encji/współudziału w wywołaniach metod - EntityManagers mogą być zadeklarowane prywatnie w każdej metodzie. Logika implementacji może być bardzo prosta.
The Answer do Twojego problemu: Zastosowanie Application-Managed Persistence Konteksty (za pośrednictwem aplikacji zarządzane EntityManagers)
Deklarują swoją EntityManager za pośrednictwem jednego z tych podejść:
// "Java EE style" declaration of EM
@PersistenceUnit(unitName="H2PU")
EntityManagerFactory emfH2;
EntityManager emH2 = emfH2.createEntityManager();
LUB
// "JSE style" declaration of EM
EntityManagerFactory emfH2 = javax.persistence.Persistence.createEntityManagerFactory("H2PU");
EntityManager emH2 = emfH2.createEntityManager();
and the same for emfOracle & emOracle.
Musisz wywołać em.close() po zakończeniu każdego EM - najlepiej przez ostatnią { } lub za pomocą instrukcji try-with-resources Java 7.
EM zarządzane przez aplikacje nadal uczestniczą w (tj. Synchronizują się z) transakcjami JTA. Dowolna liczba EM zarządzanych przez aplikacje może uczestniczyć w pojedynczej transakcji JTA, ale żadna z nich nigdy nie będzie miała kontekstu utrwalania skojarzonego lub propagowanego z żadnym z zarządzanych kontenerami EM.
Jeśli EntityManager tworzony jest poza kontekstem transakcji JTA (zanim rozpoczęto transakcji), a następnie należy go zapytać jawnie przystąpić do transakcji JTA:
// must be run from within Java EE code scope that already has a JTA
// transaction active:
em.joinTransaction();
Albo jeszcze prościej, jeśli EntityManager jest utworzony w kontekście transakcji JTA, wówczas zarządzany przez aplikację EntityManager automatycznie łączy się z implikacją transakcji JTA - nie jest wymagana żadna metoda joinTransaction().
W związku z tym systemy EM zarządzane za pomocą aplikacji mogą zawierać transakcje JTA obejmujące wiele baz danych. Oczywiście, można sprecyzowane uruchomienie lokalnego zasobu JDBC niezależnej transakcji z JTA:
EntityTransaction tx = em.getTransaction();
tx.begin();
// ....
tx.commit();
EDIT: Dodatkowe szczegóły dotyczące zarządzania transakcji z Entity Managers
UWAGA Application-Managed: próbki kodu poniżej są dla wykorzystanie edukacyjne - wypisałem je z mojej głowy, aby pomóc wyjaśnić moje punkty: & nie miałem czasu na kompilację/debugowanie/testowanie.
Domyślnym parametrem @TransactionManagement dla EJB jest TransactionManagement.CONTAINER, a domyślny parametr @TransactionAttribute dla metod EJB to TransactionAttribute.REQUIRED.
Istnieją cztery permutacje zarządzania transakcji:
A) z kontenera EJB zarządzanych transakcji JTA
Jest to korzystne podejście Java EE.
Adnotacja EJB class @TransactionManagement:
musi jawnie ustawić TransactionManagement.CONTAINER lub pominąć ją, aby niejawnie używać wartości domyślnej.
Metoda EJB @ Wartość transakcjiAttribute: musi jawnie ustawić na TransactionAttribute.REQUIRED lub pominąć, aby domniemana wartość była domyślna. (Uwaga: gdybyś miał inny scenariusz biznesowy, mógłbyś użyć TransactionAttribute.MANDATORY lub TransactionAttribute.REQUIRES_NEW, jeśli ich semantyka odpowiadała Twoim potrzebom.)
Menedżery zarządzające podmiotami zarządzanymi przez aplikacje:
muszą być tworzone za pomocą Persistence.createEntityManagerFactory ("unitName") i emf.createEntityManager(), jak opisano powyżej.
Przyłącz się do EntityManagers za pomocą transakcji JTA:
Utwórz EntityManagers w transakcyjnej metodzie EJB, a oni automatycznie dołączą do transakcji JTA. LUB jeśli EntityManagers są tworzone wcześniej, wywołaj metodę em.joinTransaction() w metodzie EJB transakcji.
Zadzwoń do EntityManager.close() po zakończeniu korzystania z nich. To wszystko, co potrzebne.
podstawowe przykłady - wystarczy użyć więcej EntityManagers dla całej transakcji wielokrotnych 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 Bean udało transakcji JTA
Zastosowanie @ TransactionManagement.BEAN.
Wprowadź interfejs użytkownika JTA UserTransaction, aby komponent bean mógł bezpośrednio oznaczać transakcje JTA.
Ręcznie zaznacz/zsynchronizuj transakcję za pomocą metody UserTransaction.begin()/commit()/rollback().
Upewnij się, że EntityManager dołącza do transakcji JTA - utwórz EM w aktywnym kontekście transakcji JTA LUB wywołaj metodę em.joinTransaction().
Przykłady:
@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 z zasobów transakcji lokalnych ręcznie kodowane (fasola udało) (nie JTA)
Wystarczy użyć interfejsu JPA EntityTransaction dla tx demarkacji (uzyskane za pośrednictwem em.getTransaction()).
przykład:
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/dla EJB ręką kodowany (POJO zarządzane) transakcji JTA
ten zakłada POJO/składnik uruchomiony w jakimś pojemniku, który ma JTA wsparcie.
Jeśli w kontenerze Java EE można użyć wtrysku zasobów Java EE interfejsu użytkownika JTA JTA.
(. Można wyraźnie odnośnika uchwyt do interfejsu JTA i do granic na niego, a następnie wywołać em.getTransaction() joinTransaction() - patrz JTA spec.)
przykład:
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
// ...
}
// ...
}
}
Czy jest to tylko błąd kopiowania i wklejania, czy też błędna nazwa jednostki znajduje się w '@ PercistenceContext'? Czy powinien to być 'FenixRadarPU'? – Magnilex
To jest błąd wklejania kopii. Już to poprawiłem. Dziękuję Ci! –
Dodaj adnotacje '@PersistenceContext (unitName =" ... ")' bezpośrednio do 'EntityManager's w klasie" Service ", aby spróbować ustalić, czy jest to problem CDI, czy problem JPA. –