Spring Data – Teil 2: JPA

4 Kommentare

Was bisher geschah

Teil 1: Spring Data Commons
Teil 2: Spring Data JPA

Nachdem wir im ersten Teil dieser Blog-Serie das Spring Data Commons Projekt vorgestellt haben, wollen wir nun das Unterprojekt Spring Data JPA genauer unter die Lupe nehmen.

JPA

JPA ist als Teil des JEE-Stacks eine standardisierte Schnittstelle, mit der POJOs in relationalen Datenbanksystemen persistiert werden können. Mit einer eigenen Abfragesprache JPQL können Datenbankabfragen unabhängig von einem konkreten SQL-Dialekt formuliert werden. Für das Verständnis von Spring Data JPA sollten daher zumindest Grundkenntnisse des JPA APIs vorhanden sein.

Spring Data JPA

Aufbauend auf dem klassischen JPA Support bietet Spring Data JPA (u.a.) ein Konzept, mit dem JPQL-Queries wesentlich einfacher implementiert werden können. Üblicherweise sind Queries parametrisiert. Vor der Ausführung einer Query schreibt der Entwickler meistens eine Menge Boilerplate-Code, um diese Parameter zu setzen. Das wird dann klassich in einem Spring-Repository in etwa so implementiert:

@Entity
@NamedQuery( name="myQuery", query = "SELECT u FROM User u where u.fullName = :fullName" )
public class User {
...
}
 
@Repository
public class ClassicUserRepository {
 
   @PersistenceContext EntityManager em;
 
   public List<User> findByFullName(String fullName) {
      TypedQuery<User> q = getEntityManger().createNamedQuery("myQuery", User.class);
 
      q.setParameter("fullName", fullName);
 
      return q.getResultList();
   }
   ...

Dieser Code lässt sich durch Nutzung des Fluent Intefaces von TypedQuery zwar noch etwas kompakter formulieren …

@Repository
public class ClassicUserRepository {
 
   @PersistenceContext EntityManager em;
 
   public List<User> findByFullName(String fullName) {
      return getEntityManger().createNamedQuery("myQuery", User.class)
         .setParameter("fullName", fullName)
         .getResultList();
   }
   ...

… dennoch schreibt man für jede Datenbankabfrage immer wieder eine Methode, die eine Liste von Parameter entgegennimmt, diese in die Query einsetzt und schließlich die Query ausführt. Mit Spring Data JPA lässt sich das wesentlich eleganter formulieren:

package repositories;
 
public interface UserRepository extends JpaRepository<User, String> {
 
   List<User> findByFullName(String fullName);
}

Die Grundidee ist die, dass man bereits aus der Signatur einer Interface(!)-Methode den bzw. die Namen des/der Query-Parameter ableiten kann. Spring liefert zur Laufzeit eine entsprechende Implementierung, die die entsprechende Query über das JPA Criteria API aufbaut und auch ausführt. Damit ist ein Großteil von Queries in der Praxis sehr schnell formuliert. Dieser Mechanismus läßt sich mit den allgemeinen Sortierungs- und Paginations-Aspekten kombinieren:

public interface UserRepository extends JpaRepository<User, String> {
 
   List<User> findByFullName(String fullName, Sort sort);
 
   List<User> findByFullName(String fullName, Pageable paging);
}

Ein weiterer Unterschied zur klassischen JPA-Entwicklung stellt die Möglichkeit dar, JPQL-Queries als Annotation an die Query-Methode zu schreiben. Üblicherweise werden die Queries mittels der @NamedQuery an die JPA-Entity annotiert und müssen über einen symbolischen Namen aufgefunden werden. Dies ist mit Spring Data JPA nicht notwendig, da die Query direkt an die Repository-Methode annotiert wird:

	@Transactional(timeout = 2, propagation = Propagation.REQUIRED)
	@Query("SELECT u FROM User u WHERE u.fullName = 'User 3'")
	List<User> findByGivenQuery();

Ich persönlich finde diesen Ansatz sehr gelungen, da auf diese Weise die Query dort formuliert wird, wo sie auch verwendet wird und nicht die JPA-Entität zumüllt.

Eine weitere nette Eigenschaft ist die, dass die Queries bereits beim Aufbau des Application Context validiert werden. So werden Syntax-Fehler in JPQL-Queries schnell gefunden. Dies geschieht bei Verwendung des reinen JPA APis sonst erst zur Laufzeit.

Beispiel

Den kompletten Source-Code findet man als Maven-Projekt auf Github. Zur Ausführung der Beispiele verwenden wir OpenJPA als JPA-Provider und HyperSQL DB als RDBMS. Der Unit-Test jpa.JpaRepoTest ist ein guter Startpunkt.

Wesentlich ist, dass man konfiguriert, aus welcher Paketstruktur die Repository-Interfaces automagisch JPA-fiziert werden sollen:

	<jpa:repositories base-package="jpa"/>

Bei mehr als einer verwendeten EntityManagerFactory kann man angeben, welche konkrete Factory die Repositories verwenden sollen.

Was gibt’s sonst noch?

Wie im ersten Teil der Blog-Serie angekündigt, habe ich nur einige Aspekte von Spring Data JPA vorgestellt. Eine vollständige Feature-Liste findet man auf der Homepage des Projekts.

Ausblick

Die Projekte Spring Data MongoDB oder Spring Data Neo4J sind gute Kandidaten für weitere Beiträge in dieser Serie.

Autor

Tobias Trelle

Tobias Trelle

Senior IT Consultant

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *