Wenn Leute neu in JPA, Hibernate oder EclipseLink sind, sind sie oft verwirrt über den Unterschied zwischen ihnen und welchen sie in ihrem Projekt verwenden sollten. Wenn du einer von ihnen bist, mach dir keine Sorgen. Es ist viel einfacher als es scheint.
Schauen wir uns zuerst die JPA-Spezifikation an.
Java Persistence API (JPA)
JPA ist eine Abkürzung, die für Java Persistence API steht. Es ist eine Spezifikation, die Teil von Java EE ist und eine API für objektrelationale Zuordnungen und zum Verwalten persistenter Objekte definiert. Sie können diese API in Java SE- und Java EE-Umgebungen verwenden.
Die Spezifikation ist derzeit in Version 2.2 verfügbar. Sie können das Dokument unter https://jcp.org/en/jsr/detail?id=338 herunterladen. Die API-JAR ist unter den folgenden Maven-Koordinaten verfügbar:
<dependency> <groupId>javax.persistence</groupId> <artifactId>javax.persistence-api</artifactId> <version>2.2</version></dependency>
JPA selbst bietet keine Implementierungsklassen. Die API-JAR enthält nur eine Reihe von Schnittstellen, mit denen Sie Ihre Persistenzschicht implementieren können. Sie können JPA jedoch nicht alleine verwenden. Sie benötigen einen JPA-Anbieter, der die Spezifikation implementiert. Es stehen mehrere Optionen zur Verfügung. Die beliebtesten sind Hibernate und EclipseLink. Aber dazu später mehr.
Bis vor kurzem wurde JPA von einer Expertengruppe nach dem Java Community Process (JCP) verwaltet und entwickelt. Das änderte sich, als Oracle ankündigte, alle Java EE-Spezifikationen an die Eclipse Foundation zu übertragen. Wir befinden uns jetzt mitten im Übergangsprozess und ein neuer Spezifikationsprozess wird in Kürze definiert.
Was wird in der JPA-Spezifikation definiert
Die Spezifikation definiert die meisten Funktionen, die ich in den Tutorials und Videos auf dieser Website erläutert habe. Sie können sie mit allen kompatiblen JPA-Implementierungen verwenden.
Schauen wir uns einige der wichtigsten an.
Bootstrapping und grundlegende Entitätszuordnungen
Bevor Sie JPA verwenden können, müssen Sie es zu Ihrem Projekt hinzufügen, eine Persistenzeinheit konfigurieren, Entitäten Ihren Datenbanktabellen zuordnen und bootstrappen. Sie wissen wahrscheinlich bereits, wie das geht, und ich habe es in meinem Artikel Erste Schritte mit dem Ruhezustand ausführlich erklärt.
Lassen Sie uns diesen Teil hier überspringen und über die interessanteren Funktionen sprechen.
Zuordnungen zuordnen
Mit JPA können Sie nicht nur einfache Entitätsattribute Datenbankspalten zuordnen, sondern auch Zuordnungen zwischen Datenbanktabellen zu Entitätsattributen zuordnen.
@Entitypublic class Review {...@ManyToOneprivate Book book;...}
Dadurch ist Ihr Entitätsmodell häufig sehr komfortabel zu verwenden, da Sie nur eine Getter-Methode für eine Entität aufrufen müssen, um die zugehörigen Entitäten zu laden. Im Hintergrund führt der Persistenzanbieter alle erforderlichen Datenbankoperationen aus, um die Zuordnung abzurufen und zu verwalten.
So komfortabel dies auch sein mag, diese Funktion verursacht häufig Leistungsprobleme. Bevor Sie mit dem Modellieren von Zuordnungen zwischen Ihren Entitäten beginnen, stellen Sie sicher, dass Sie die Auswirkungen der FetchTypes von JPA verstehen, um n + 1 Select-Probleme zu vermeiden.
JPQL und native Abfragen
JPA definiert eine eigene Abfragesprache namens JPQL. Es ähnelt SQL, ermöglicht es Ihnen jedoch, Abfragen basierend auf dem zugeordneten Domänenmodell anstelle des Tabellenmodells der Datenbank zu definieren.
Das folgende Code-Snippet zeigt eine einfache JPQL-Abfrage. Sie können eine Ad-hoc-Abfrage definieren, indem Sie die createQuery-Methode auf der EntityManager-Seite aufrufen. Wie Sie sehen können, sieht die Syntax SQL sehr ähnlich. Wenn Sie mit JPQL nicht vertraut sind, schauen Sie sich bitte meinen JPQL-Leitfaden an, in dem ich die Syntax und die Funktionen ausführlich erläutere.
TypedQuery<Book> q = em.createQuery("SELECT b FROM Book b WHERE b.id = :id", Book.class);q.setParameter("id", 1L);Book b = q.getSingleResult();
Wenn Sie eine solche Abfrage ausführen, interpretiert Ihr Persistenzanbieter die JPQL-Anweisung und generiert eine SQL-Abfrage dafür. Auf diese Weise passt der Persistenzanbieter die Abfrage an den datenbankspezifischen SQL-Dialekt an und verbessert die Portabilität Ihrer Anwendung.
Leider beschränkt sich dies auch auf die in der Spezifikation definierten oder von Ihrem Persistenzanbieter eigenständig unterstützten Abfragefunktionen. Dieser Funktionsumfang ist deutlich kleiner als der von SQL angebotene und enthält keine proprietären Datenbankfunktionen.
Dies bedeutet jedoch nicht, dass Sie keine erweiterten oder komplexen Abfragen mit JPA verwenden können. Es ist als undichte Abstraktion konzipiert und ermöglicht es Ihnen, native SQL-Abfragen auszuführen. Diese werden nicht von Ihrem Persistenzanbieter analysiert, und Sie können alle von Ihrer Datenbank unterstützten Funktionen verwenden. Beachten Sie jedoch, dass dies die Portabilität Ihrer Datenbank beeinträchtigen kann.
Das Ausführen einer nativen Abfrage ist ziemlich einfach. Sie müssen nur die createNativeQuery-Methode anstelle der createQuery-Methode in Ihrem EntityManager mit einer nativen SQL-Abfrage aufrufen.
Query q = em.createNativeQuery("SELECT * FROM book b WHERE id = :id", Book.class);q.setParameter("id", 1L);Book b = (Book) q.getSingleResult();
Benutzerdefinierte Datentypen
Die JPA-Spezifikation definiert die Zuordnung für die meisten Standardtypen, ohne Sie darauf zu beschränken. Seit JPA 2.1 können Sie benutzerdefinierte Datentypen problemlos mit einem AttributeConverter unterstützen. Sie müssen nur die AttributeConverter Schnittstelle implementieren und die Klasse mit einer @Converter Annotation @Converter .
Hier ist ein Beispiel für einen Attributkonverter, der eine benutzerdefinierte Zuordnung für meine AuthorStatus-Enumeration definiert.
@Converter(autoApply = true)public class AuthorStatusConverter implements AttributeConverter<AuthorStatus, String> {Logger log = Logger.getLogger(AuthorStatusConverter.class.getSimpleName());@Overridepublic String convertToDatabaseColumn(AuthorStatus status) {switch (status) {case NOT_PUBLISHED:logDbConversion(status, "N");return "N";case PUBLISHED:logDbConversion(status, "P");return "P";case SELF_PUBLISHED:logDbConversion(status, "S");return "S";default:throw new IllegalArgumentException("AuthorStatus not supported.");}}@Overridepublic AuthorStatus convertToEntityAttribute(String dbData) {switch (dbData) {case "N":logEntityConversion(AuthorStatus.NOT_PUBLISHED, "N");return AuthorStatus.NOT_PUBLISHED;case "P":logEntityConversion(AuthorStatus.PUBLISHED, "P");return AuthorStatus.PUBLISHED;case "S":logEntityConversion(AuthorStatus.SELF_PUBLISHED, "S");return AuthorStatus.SELF_PUBLISHED;default:throw new IllegalArgumentException("AuthorStatus not supported.");}}private void logDbConversion(AuthorStatus status, String dbData) {log.debug("Convert AuthorStatus enum to .");}private void logEntityConversion(AuthorStatus status, String dbData) {log.debug("Convert DB value to AuthorStatus enum .");}}
EclipseLink und Hibernate
Wie bereits erwähnt, benötigen Sie einen JPA-Anbieter, wenn Sie die JPA-Spezifikation in Ihrem Projekt verwenden möchten. Es implementiert die Schnittstellen wie in der Spezifikation definiert. Die beliebtesten sind EclipseLink und Hibernate.
Ein Vorteil der standardisierten API von JPA besteht darin, dass Sie die Implementierung nur zur Laufzeit hinzufügen müssen und sie durch eine andere ersetzen können, ohne den Code zu ändern. Die standardisierte API macht EclipseLink und Hibernate austauschbar.
Also, warum brauchen Sie verschiedene Implementierungen?
Die JPA-Implementierungen werden von unabhängigen Teams verwaltet, und Sie können dasjenige auswählen, das die beste Leistung oder Unterstützung für Ihre Anwendung und Ihren Technologie-Stack bietet. Sie unterscheiden sich auch durch die Bereitstellung zusätzlicher, nicht standardmäßiger Funktionen. Dies wird oft genutzt, um Innovationen voranzutreiben. Die heute beliebte, proprietäre Funktion könnte der erste Schritt zur nächsten Ergänzung des JPA-Standards sein. Die Verwendung einer dieser proprietären Funktionen macht es offensichtlich viel schwieriger, eine bestimmte JPA-Implementierung zu ersetzen.
EclipseLink
EclipseLink ist die Referenzimplementierung von JPA und implementiert JPA Version 2.2. Es war eines der ersten Projekte, das Teil von EE4J wurde.
Der einfachste Weg, EclipseLink zu Ihrem Projekt hinzuzufügen, besteht darin, die folgenden Maven-Koordinaten zu verwenden.
<dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>eclipselink</artifactId> <version>2.7.1</version></dependency>
Interessante proprietäre Funktionen
Zusätzlich zu den vom JPA-Standard definierten Funktionen bietet EclipseLink auch einige interessante proprietäre Funktionen wie:
- Behandlung von Datenbankänderungsereignissen
- Zusammengesetzte Persistenzeinheiten zur Zuordnung von Entitäten zu Tabellen in mehreren Datenbanken
- Unterstützung für Mandantenfähigkeit
Hibernate
Hibernate ist Red Hats sehr beliebte Implementierung der JPA-Spezifikation. Es implementiert fast alle von JPA 2.2 definierten Funktionen und wird in Kürze eine vollständig kompatible Version veröffentlichen.
Die folgende Maven-Abhängigkeit fügt Ihrem Projekt den Ruhezustand hinzu.
<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-core</artifactId><version>5.1.11</version></dependency>
Interessante proprietäre Funktionen
Ähnlich wie EclipseLink bietet Hibernate eine Reihe interessanter proprietärer Funktionen, wie zum Beispiel:
- Erweiterte Unterstützung für natürliche IDs
- Laden mehrerer Entitäten anhand ihres Primärschlüssels
- Verwaltung von Erstellungs- und Aktualisierungszeitstempeln
- Verbinden nicht zugeordneter Entitäten in Abfragen
- Unterstützung für Mandantenfähigkeit