Blog
JPA-Implementierungsmuster: Datenzugriffs-Objekte

JPA, kurz für Java Persistence API, ist Teil der Java EE 5-Spezifikation und wurde von Hibernate, TopLink, EclipseLink, OpenJPA und einer Reihe anderer objektrelationaler Mapping-Frameworks (ORM) implementiert. Da JPA ursprünglich als Teil der EJB 3.0-Spezifikation entwickelt wurde, können Sie es innerhalb einer EJB 3.0-Anwendung verwenden. Aber es funktioniert genauso gut außerhalb von EJB 3.0, zum Beispiel in einer Spring-Anwendung. Und wenn sogar Gavin King, der Entwickler von Hibernate, in der zweiten Ausgabe von Hibernate in Action, auch bekannt als Java Persistence with Hibernate, die Verwendung von JPA empfiehlt, dann ist es offensichtlich, dass JPA nicht mehr wegzudenken ist. Wenn Sie erst einmal Ihre Angst vor Anmerkungen überwunden haben ;-), werden Sie feststellen, dass es jede Menge Literatur gibt, die die Objekte und Methoden innerhalb der API erklärt, die Art und Weise, wie diese Objekte zusammenarbeiten und wie Sie erwarten können, dass sie implementiert werden. Und wenn Sie sich an Programme im Hallo-Welt-Stil halten, scheint das alles ziemlich einfach zu sein. Aber wenn Sie anfangen, Ihre erste echte Anwendung zu schreiben, werden Sie feststellen, dass die Dinge nicht so einfach sind. Die von JPA gebotene Abstraktion ist ziemlich undurchsichtig und hat Auswirkungen auf größere Teile Ihrer Anwendung als nur Ihre Data Access Objects (DAOs) und Ihre Domänenobjekte. Sie müssen entscheiden, wie Sie Transaktionen, Lazy Loading, losgelöste Objekte (denken Sie an Web-Frameworks), Vererbung und vieles mehr handhaben. Und es stellt sich heraus, dass die Bücher und Artikel Ihnen hier nicht wirklich weiterhelfen.
Zumindest habe ich das festgestellt, als ich zum ersten Mal wirklich mit JPA gearbeitet habe. In den kommenden Wochen möchte ich über die Entscheidungen sprechen, die ich getroffen habe und warum ich sie getroffen habe. Wenn ich damit fertig bin, werden wir eine Reihe von JPA-Implementierungsmustern haben, die ich nicht zu bescheiden nennen möchte ;-)
Brauchen wir wirklich eine DAO?
Beginnen wir also mit dem, was Sie wahrscheinlich als erstes in Ihrer JPA-Anwendung schreiben würden: das Data Access Object (DAO). Ein interessanter Punkt, mit dem wir uns befassen sollten, bevor wir beginnen, ist die
- Anstatt den richtigen EntityManager Methode jedes Mal auszuwählen, wenn Sie Daten speichern oder laden wollen, entscheiden Sie einmal, welche Methode Sie verwenden möchten, und Sie und Ihr gesamtes Team können sich problemlos an diese Entscheidung halten.
- Sie können bestimmte Operationen für bestimmte Entitätstypen verbieten. Zum Beispiel möchten Sie vielleicht nicht, dass Ihr Code Protokolleinträge entfernt. Wenn Sie DAOs verwenden, fügen Sie einfach keine remove-Methode zu Ihrer LogEntry-DAO hinzu.
- Theoretisch könnten Sie durch die Verwendung von DAOs zu einem anderen Persistenzsystem (wie einfaches JDBC oder iBATIS) wechseln. Aber da JPA eine so undurchlässige Abstraktion ist, halte ich das selbst für eine wenig komplexe Anwendung für nicht realistisch. Sie erhalten einen einzigen Einstiegspunkt, an dem Sie Tracing-Funktionen hinzufügen oder Leistungsstatistiken führen können.
- Sie können alle Abfragen zu einem bestimmten Entitätstyp zentralisieren, anstatt sie über Ihren Code zu verstreuen. Sie könnten benannte Abfragen verwenden, um die Abfragen beim Entitätstyp zu belassen, aber Sie bräuchten trotzdem eine zentrale Stelle, an der die richtigen Parameter gesetzt werden. Es scheint einfacher zu sein, sowohl die Abfrage als auch den Code, der die Parameter setzt, und die Umwandlung in den richtigen Rückgabetyp in der DAO zu platzieren. Zum Beispiel:
public List findExecutingChangePlans() { Abfrage query = entityManager.createQuery( "SELECT plan FROM ChangePlan plan where plan.state = 'EXECUTING'"); return (Liste) query.getResultList(); }
Wenn Sie also beschließen , DAOs zu verwenden, wie gehen Sie dann vor, um sie zu schreiben? Der hervorgehobene (fett gedruckte) Kommentar in der Javadoc für Spring's
Das typsichere generische DAO-Muster
Da jedes DAO viele Funktionen mit den anderen DAOs teilt, ist es sinnvoll, eine Basisklasse mit den gemeinsamen Funktionen zu haben und diese dann für jedes spezifische DAO als Unterklasse zu verwenden. Es gibt eine Menge Blogs über ein solches typsicheres generisches DAO-Muster und Sie können sogar Code von Google Code herunterladen. Wenn wir Elemente aus all diesen Quellen kombinieren, erhalten wir das folgende JPA-Implementierungsmuster für DAOs.
Die Entitätsklasse
Nehmen wir an, wir möchten die folgende Order-Klasse persistieren:
@Entität
@Table(name = "ORDERS")
public class Order {
@Id
@GeneratedValue
private int id;
private String customerName;
Privat Datum Datum;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getKundenname() { return Kundenname; }
public void setCustomerName(String customerName) { this.customerName = customerName; }
public Date getDate() { return date; }
public void setDate(Date date) { this.date = date;}
}
Machen Sie sich nicht zu viele Gedanken über die Details dieser Klasse. Wir werden die Einzelheiten in anderen JPA-Implementierungsmustern wieder aufgreifen. Die tt>@Table Annotation ist da, weil ORDER ein reserviertes Schlüsselwort in SQL ist.
Die DAO-Schnittstellen
Zunächst definieren wir eine generische DAO-Schnittstelle mit den Methoden, die alle DAOs gemeinsam haben sollen:
public interface Dao {
void persist(E entity);
void remove(E entity);
E findById(K id);
}
Der erste Typparameter, K, ist der als Schlüssel zu verwendende Typ und der zweite Typparameter, E, ist der Typ der Entität. Neben den grundlegenden Methoden persist, remove und findById können Sie auch eine Methode List findAll() hinzufügen. Aber wie die Entitätsklasse selbst werden wir auch die DAO-Methoden in späteren JPA-Implementierungsmustern wieder aufgreifen. Dann definieren wir eine Unterschnittstelle für jeden Entitätstyp, den wir persistieren möchten, und fügen alle gewünschten entitätsspezifischen Methoden hinzu. Wenn wir zum Beispiel alle Bestellungen abfragen möchten, die seit einem bestimmten Datum hinzugefügt wurden, können wir eine solche Methode hinzufügen:
public interface OrderDao extends Dao {
Liste findOrdersSubmittedSince(Date date);
}
Die Basis-DAO-Implementierung
Der dritte Schritt besteht darin, eine Basis-Implementierung der JPA DAO zu erstellen. Sie wird eine Basisimplementierung aller Methoden der Standard-Dao-Schnittstelle haben, die wir in Schritt 1 erstellt haben:
public abstract class JpaDao implements Dao {
protected Klasse entityClass;
@PersistenzKontext
protected EntityManager entityManager;
public JpaDao() {
ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
this.entityClass = (Class) genericSuperclass.getActualTypeArguments()[1];
}
public void persist(E entity) { entityManager.persist(entity); }
public void remove(E entity) { entityManager.remove(entity); }
public E findById(K id) { return entityManager.find(entityClass, id); }
}
Der größte Teil der Implementierung ist ziemlich einfach. Einige Punkte sind jedoch zu beachten:
- Der Konstruktor von JpaDao enthält die von meinem Kollegen Arjan Blokzijl vorgeschlagene Methode zur Verwendung von Reflection, um die Entitätsklasse zu erhalten.
- Die Annotation @PersistenceContext veranlasst den EJB 3.0-Container oder Spring, den Entity Manager zu injizieren.
- Die Felder entityManager und entityClass sind geschützt, so dass Unterklassen, d.h. bestimmte DAO-Implementierungen, auf sie zugreifen können.
Die spezifische DAO-Implementierung
Und schließlich erstellen wir eine solche spezifische DAO-Implementierung. Sie erweitert die JPA DAO-Basisklasse und implementiert die spezifische DAO-Schnittstelle:
public class JpaOrderDao extends JpaDao implements OrderDao {
public List findOrdersSubmittedSince(Date date) {
Abfrage q = entityManager.createQuery(
"SELECT e FROM " + entityClass.getName() + " e WHERE date >= :date_since");
q.setParameter("date_since", date);
return (Liste) q.getResultList();
}
}
Verwendung des DAO
Wie Sie eine Referenz auf eine Instanz Ihres OrderDao erhalten, hängt davon ab, ob wir EJB 3.0 oder Spring verwenden. In EJB 3.0 würden wir eine Annotation wie diese verwenden:
@EJB(name="orderDao") private OrderDao orderDao;
während wir in Spring die XML-Bean-Dateien verwenden können, oder wir können das Autowiring wie folgt nutzen:
@Autowired public OrderDao orderDao;
Sobald wir einen Verweis auf die DAO haben, können wir sie auf jeden Fall wie folgt verwenden:
Bestellung o = new Bestellung();
o.setCustomerName("Peter Johnson");
o.setDate(new Datum());
orderDao.persist(o);
Wir können aber auch die entitätsspezifische Abfrage verwenden, die wir der OrderDao-Schnittstelle hinzugefügt haben:
List orders = orderDao.findOrdersSubmittedSince(date);
for (Order each : orders) {
System.out.println("order id = " + each.getId());
}
Mit diesem typsicheren DAO-Muster erhalten wir die folgenden Vorteile:
- Keine direkte Abhängigkeit vom Client-Code von der JPA-Api.
- Typsicherheit durch die Verwendung von Generika. Alle Casts, die noch durchgeführt werden müssen, werden in der DAO-Implementierung behandelt.
- Ein logischer Ort, um den gesamten entitätsspezifischen JPA-Code zu gruppieren.
- Ein Ort zum Hinzufügen von Transaktionsmarkierungen, Debugging, Profilerstellung usw. Wie wir später sehen werden, müssen wir allerdings auch in anderen Teilen unserer Anwendungen Transaktionsmarkierungen hinzufügen.
- Eine Klasse, die Sie testen können, wenn Sie den Code für den Datenbankzugriff testen. Wir werden dieses Thema in einem späteren JPA-Implementierungsmuster erneut aufgreifen.
Ich hoffe, dies überzeugt Sie davon, dass Sie DAOs mit JPA benötigen. :-) Und damit ist das erste JPA-Implementierungsmuster abgeschlossen. Im nächsten Blog dieser Serie werden wir auf diesem Beispiel aufbauen und das nächste Muster besprechen. In der Zwischenzeit würde ich gerne von Ihnen hören, wie Sie Ihre DAOs schreiben! Eine Liste aller Blogs zu JPA-Implementierungsmustern finden Sie in der Zusammenfassung zu JPA-Implementierungsmustern oder in allen Beiträgen dieser Kategorie.
Verfasst von
Vincent Partington
Unsere Ideen
Weitere Blogs
Contact



