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.
Kategorie:
English
Deutsch 

Tobias,
has this annotation
@Target({TYPE})While I agree with your preference of having the query on the method, the JPA Interface doesnt allow this.
I wonder if a backwards compatible change to the spec allowing this at method level would be possible.
The reason for introducing
.
@Queryis that we support some additional quirks (defining a count query e.g.) in it besides the fact which Fabian mentioned. Regarding the potential change of being able to use@NamedQueryon a method I don’t think this is gonna happen as this does not make sense in a pure JPA context and I don’t expect the expert group to add stuff to it just because some arbitrary framework would like to see thisGood post, I didn’t know much about Spring JPA support but doesn’t it like combining JEE with Spring ?