Ich versuche, eine alte Anwendung umzufunktionieren, um EJB3 mit JPA zu verwenden.Wie schichte ich EJB3 und Servlets korrekt?
Wir haben zwei Client-Schichten (eine Servlet-Basis, einem nicht), das in eine Delegiertenschicht nennen beide, die eine EJB-Schicht aufruft, die wiederum eine DAO aufruft. Das EJB ist EJB2 (Bean-Managed Persistence), und das DAO verwendet handgenerierte SQL-Abfragen, die Transaktionen festschreiben und Verbindungen manuell schließen.
Ich möchte die EJB2 mit EJB3 ersetzen und alle DAO ändern JPA zu verwenden.
begann ich durch den Austausch des EJB2 Code mit einem EJB3 mit Container-gesteuerten Transaktionen ab. Da Hibernate Criteria so einfach ist, und kann der EntityManager injiziert wird, kann ich so etwas tun:
@Stateless
public class NewSelfcareBean implements SelfcareTcApi {
@PersistenceContext(unitName="core")
EntityManager em;
public BasicAccount getAccount(String id) {
Criteria crit = getCriteria(BasicAccount.class);
crit.add(Restrictions.eq("id", id));
BasicAccount acc = (BasicAccount) crit.uniqueResult();
}
}
Keine Notwendigkeit für eine separate DAO Schicht. Das Konto Objekt sieht ein bisschen wie folgt aus:
@Entity
@Table(name="er_accounts")
public class BasicAccount {
@OneToMany(mappedBy="account", fetch=FetchType.LAZY)
protected List<Subscription> subscriptions;
}
Aber in der Servlet-Schicht, wo ich die EJB rufen Sie das Konto-Objekt zu erhalten, habe ich eine Antwort bauen wollen, die vielleicht (oder auch nicht) umfassen Kind Abonnements von die BasicAccount:
die Servlet-Schicht sieht wie folgt aus:
ResponseBuilder rb;
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
...
Account acc = getDelegateLayer().getAccount();
rb.buildSubscriptionResponse(acc.getSubscriptions());
...
}
Offensichtlich ist dies nicht funktioniert, da die Transaktion und EntityManager haben durch die Zeit, die wir auf die Servlet-Schicht wieder geschlossen worden ist - ich LazyInitializationException bekommen .
So kann ich ein paar Optionen:
- ServletFilter Transaktionen manuell zu verwalten. Das bedeutet, dass ich die Vorteile von EJB-Container-verwalteten Transaktionen verliere, nicht wahr? Außerdem müsste ich einen anderen Filtermechanismus auf dem anderen Client implementieren (nicht am Ende der Welt).
- Verwenden Sie stattdessen eine Stateful Session Bean, dann kann ich erweiterten Persistenzkontext verwenden. Aber ich brauche keine Stateful Bean, da zwischen den Transaktionen keine Daten gespeichert werden. Das würde den Server unnötig belasten und ich würde Stateful für etwas verwenden, für das es nicht wirklich entwickelt wurde.
- Anruf
Hibernate.init(acc.getSubscriptions())
- dies funktioniert, muss aber in der EJB getan werden. Angenommen, ich verwende die Bean für eine andere Client-Methode, die die Subskriptionen nicht benötigt? Ein unnötiger DB-Aufruf. - Verwenden Sie EAGER FetchType auf meinem Kontoobjekt. Schlecht für die Leistung und erstellt unnötige DB-Last.
Keine dieser Optionen scheint etwas Gutes.
Fehle ich etwas? Wie soll das gemacht werden? Ich kann nicht die erste Person sein, um dieses Problem zu haben ...
Abfrage einer Liste von Abonnements, die einem Konto zugeordnet sind, in einem separaten Aufruf an das jeweilige EJB (separate Transaktion). Sie können eine Subskriptionsliste, die auf einem Konto basiert, während einer Transaktion abrufen und von der EJB-Methode in einem separaten Aufruf zurückgeben. – Tiny
Warum nicht zwei Methoden erstellen, eine für nur den BasicAccount, eine andere für BasicAccount mit seinem Subscribe prefetched? Lassen Sie Ihre Anwendung wählen Sie dann die, die es benötigt – Chris
@Chris, könnte dies funktionieren - ich könnte sogar nur eine Methode verwenden, aber eine boolesche Flag angeben, ob ich Abonnements will. Aber es hat einen schlechten Code-Geruch. Gibt es eine JPA, die Hibernate.initialize() entspricht? – mdarwin