Hibernate-Tipps: Einfache Möglichkeiten zur Abbildung nativer Abfrageergebnisse auf Entitäten

Neuigkeiten von der Insel  –  3 Kommentare

Die Hibernate-Tipps-Serie bietet schnelle und einfache Lösungen zu verbreiteten Hibernate-Fragen. Dieses Mal geht es um Ergebnisse komplexer Abfragen und deren Abbildung auf managed Entitäten.

Ich musste meine Abfrage als native SQL Abfrage implementieren, da sie zu komplex für JPQL ist. Gibt es eine Möglichkeit das Abfrageergebnis auf Entitäten abzubilden, ohne diese separat laden zu müssen?

Wenn die Abfrage alle von der Entität abgebildeten Datenbankspalten selektiert, gibt es 2 Möglichkeiten das Ergebnis auf Entitäten abzubilden:

  1. Wenn die Ergebnismenge dieselben Spaltennamen verwendet wie die durch die Entität abgebildete Datenbanktabelle, kann eine einfache, implizite Abbildung verwendet werden.
  2. Wenn die Spaltennamen der Ergebnismenge von denen der Datenbanktabelle abweichen, muss eine explizite Abbildung definiert werden.

Für die meisten Abfragen stellt eine implizite Umwandlung des Abfrageergebnisses die einfachste und schnellste Lösung dar. Dazu muss lediglich die Klasse der selektierten Entität als zusätzlicher Parameter an die createNativeQuery Methode übergeben werden.

Book b = (Book) em.createNativeQuery("SELECT * FROM book b WHERE id = 1",
Book.class).getSingleResult();

Der EntityManager verwendet dabei die für die Entität definierten Abbildungen der Spaltennamen auf deren Eigenschaften. Daher müssen die Spaltennamen der Ergebnismenge der nativen Abfrage den Spaltennamen der durch die Entität abgebildeten Datenbanktabelle entsprechen.

Wenn die Spaltennamen des Abfrageergebnisses von denen der Tabelle abweichen, kann eine explizite Abbildung mit Hilfe einer @SqlResultSetMapping Annotation definiert werden.

@SqlResultSetMapping(
name = "BookMapping",
entities = @EntityResult(
entityClass = Book.class,
fields = {
@FieldResult(name = "id", column = "id"),
@FieldResult(name = "version", column = "version"),
@FieldResult(name = "title", column = "title"),
@FieldResult(name = "publishingDate",
column = "publishingDate"),
@FieldResult(name = "publisher", column = "publisherid")}))

Dabei muss der Name, über den das Mapping später referenziert werden kann, und eine @EntityResul-Annotation angegeben werden. Die @EntityResult-Annotation spezifiziert, welche Klasse für jeden Datensatz instanziiert werden soll und beschreibt über eine Liste von @FieldResult-Annotationen wie die Spalten des Ergebnisses auf die Eigenschaften der Entität abgebildet werden.

Um diese Abbildung zu verwenden, muss dann lediglich der im @SqlResultSetMapping definierte Name als 2. Parameter an die createNativeQuery-Methode übergeben werden.

em.createNativeQuery("SELECT * FROM book b WHERE id = 1", 
"BookMapping").getSingleResult();

Auf diese Art und Weise kann das Ergebnis einer nativen SQL Abfrage auch auf mehrere Entitäten oder auf nicht durch den Persistenzkontext verwaltete POJOs abgebildet werden.

Mehr als 70 solcher Rezepte zu Themen wie einfache und komplexe Mappingdefinitionen, Logging, Unterstützung von Java 8, Caching sowie die statische und dynamische Erzeugung von Abfragen gibt es in meinem Buch "Hibernate Tips: More than 70 solutions to common Hibernate problems". Es ist als Taschenbuch und E-Book auf Amazon und als PDF auf hibernate-tips.com erhältlich.