Blog

Fortgeschrittenes Hibernate, Maps Teil 2: Abfrage per Lazy Map

Maarten Winkels

Aktualisiert Oktober 23, 2025
5 Minuten

Hibernate ist ein sehr ausgereiftes und funktionsreiches Produkt, mit dem Sie viele grundlegende oder fortgeschrittene Probleme lösen können. Eine seiner Hauptfunktionen ist die Fähigkeit, Sammlungen abzubilden. Wenn eine Entität eine Sammlung, z.B. eine Map, enthält, bildet Hibernate die Entität und die Map auf eine relationale Struktur (eine Reihe von Tabellen) ab und ermöglicht es der Anwendung, durch die Sammlung zu navigieren, ohne explizite JDBC-Aufrufe tätigen zu müssen. Hibernate führt unter der Haube die richtigen Abfragen durch, um die Daten abzurufen oder sogar Zeilen in der Datenbank zu aktualisieren, je nachdem, welche Änderungen die Anwendung an der Datenstruktur im Speicher vorgenommen hat. Was aber, wenn Sie eine sehr große Sammlung haben, die Sie nach einem einzelnen Eintrag durchsuchen müssen? Nehmen wir zum Beispiel an, das System soll die Postleitzahl einer bestimmten Straße in einer Stadt abrufen. Die gesamte Sammlung von Straßen und Postleitzahlen abzurufen, nur um die Postleitzahl für eine einzelne Straße herauszufinden, ist keine sehr attraktive Idee. Der normale Ansatz wäre wahrscheinlich die Verwendung einer DAO und eine explizite Abfrage. Abgesehen von dem Problem, dass das DAO-Objekt in die Entität City injiziert werden muss, ist die Verwendung einer Lazy Map eine elegantere Lösung.

Schauen wir uns einige Code-Beispiele an.

public class City {
  private Map postalCodes;
  public void add(Straße Straße, Postleitzahl) {
  postalCodes.put(street, code);
  }
  public PostalCode getPostalCode(Street street) {
  return postalCodes.get(Straße);
  }
}
public class Straße {
  public String name;
}
public class PostalCode {
  public String code;
}

Einige wesentliche Teile des Codes (wie Konstruktoren und ähnliches) wurden der Einfachheit halber weggelassen. Wie können wir nun diese Strukturen abbilden? Schauen wir uns die Hibernate-Zuordnung an.

  



    

 

Die Karte wird als Sammlung von Composites abgebildet. Beachten Sie auch die Einstellung lazy="extra", die für die Karte verwendet wird. Wenn wir den folgenden Test ausführen

Stadt city = new City("Nijmegen");
city.add(new Street("KronenburgerSingel"), new PostalCode("6511 AR"));
city.add(new Street("Markt"), new PostalCode("6511 AA"));
city.add(new Street("Keizer Karel Plein"), new PostalCode("6511 BD"));
Session session = factory.openSession();
Transaktionstransaktion = session.beginTransaction();
session.save(city);
transaction.commit();
session.close();
session = factory.openSession();
Stadt nijmegen = (Stadt) session.createCriteria(City.class).add(Restrictions.eq("name", "Nijmegen")).uniqueResult();
assertEquals("6511 AA", nijmegen.getPostalCode(new Street("Markt")).code);
assertEquals("6511 BD", nijmegen.getPostalCode(new Street("Keizer Karel Plein")).code);
assertEquals("6511 AA", nijmegen.getPostalCode(new Street("Markt")).code);

Die Konsole zeigt Folgendes an: DEBUG SchemaExport:301 - create table City (id bigint generated by default as identity (start with 1), name varchar(255), primary key (id)) DEBUG SchemaExport:301 - create table postalCodes (CITY_FK bigint not null, code varchar(255), STREET varchar(255) not null, primary key (CITY_FK, STREET)) DEBUG SchemaExport:301 - alter table postalCodes add constraint FK84186A1B96E53CE4 foreign key (CITY_FK) references City ... DEBUG SQL:346 - insert into City (name, id) values (?, null) DEBUG StringType:80 - binding 'Nijmegen' to parameter: 1 DEBUG SQL:346 - call identity() DEBUG SQL:346 - insert into postalCodes (CITY_FK, STREET, code) values (?, ?, ?) DEBUG LongType:80 - binding '1' to parameter: 1 DEBUG StringType:80 - binding 'Markt' to parameter: 2 DEBUG StringType:80 - binding '6511 AA' to parameter: 3 DEBUG SQL:346 - insert into postalCodes (CITY_FK, STREET, code) values (?, ?, ?) DEBUG LongType:80 - binding '1' to parameter: 1 DEBUG StringType:80 - binding 'Keizer Karel Plein' to parameter: 2 DEBUG StringType:80 - binding '6511 BD' to parameter: 3 DEBUG SQL:346 - insert into postalCodes (CITYFK, STREET, code) values (?, ?, ?) DEBUG LongType:80 - binding '1' to parameter: 1 DEBUG StringType:80 - binding 'KronenburgerSingel' to parameter: 2 DEBUG StringType:80 - binding '6511 AR' to parameter: 3 ... DEBUG SQL:346 - select this.id as id00, this_.name as name00 from City this where this.name=? DEBUG StringType:80 - binding 'Nijmegen' to parameter: 1 DEBUG LongType:122 - returning '1' as column: id00 DEBUG StringType:122 - returning 'Nijmegen' as column: name00 DEBUG SQL:346 - select code from postalCodes where CITY_FK =? and STREET =? DEBUG LongType:80 - binding '1' to parameter: 1 DEBUG StringType:80 - binding 'Markt' to parameter: 2 DEBUG StringType:122 - returning '6511 AA' as column: code DEBUG SQL:346 - select code from postalCodes where CITY_FK =? and STREET =? DEBUG LongType:80 - binding '1' to parameter: 1 DEBUG StringType:80 - binding 'Keizer Karel Plein' to parameter: 2 DEBUG StringType:122 - returning '6511 BD' as column: code DEBUG SQL:346 - select code from postalCodes where CITY_FK =? and STREET =? DEBUG LongType:80 - binding '1' to parameter: 1 DEBUG StringType:80 - binding 'Markt' to parameter: 2 DEBUG StringType:122 - returning '6511 AA' as column: code Zunächst werden die beiden Tabellen erstellt. Die zweite Tabelle stellt die Map mit den beiden darin enthaltenen Komponenten dar. Die PK dieser Tabelle ist der FK der enthaltenen Entität und die Spalten stellen den Schlüssel in der Map dar, in diesem Fall die Spalte STREET, die auf die Eigenschaft Street.name abbildet. Die Zeilen werden wie erwartet eingefügt. Wenn wir nun die Daten abfragen (wir verwenden eine neue Sitzung und eine neue Entitätsinstanz), führt jeder Zugriff auf die Map zu einer neuen Abfrage in der Datenbank. Wenn wir dieselbe Anfrage zweimal stellen, wird dieselbe Abfrage zweimal ausgeführt. Das liegt daran, dass Hibernate die Map nicht im Speicher hält. Bei großen Sammlungen kann dieses Verhalten sehr nützlich sein. Es gibt zwei Nachteile bei diesem Ansatz:

  1. Eine mit Hibernate gemappte Sammlung muss immer eine Eigentümer-Entität haben. Die Map kann keine eigenständige Entität sein und sie kann nicht von mehreren Entitäten gemeinsam genutzt werden. Dies kann dazu führen, dass die relationalen Strukturen vervielfacht werden, wenn die Daten in mehreren Entitäten verwendet werden.
  2. Eine extra lazy Map kann nicht auf extra lazy Weise aktualisiert werden. Das bedeutet, dass das Hinzufügen eines Eintrags zur Map oder das Entfernen eines Eintrags aus der Map dazu führt, dass Hibernate die gesamte Map in den Speicher lädt. Außerdem kann Hibernate bei der Verwendung von Komponenten keine Änderungen an diesen Objekten erkennen, so dass dieser Code
    session = factory.openSession();
    Transaktion = session.beginTransaction();
    stad = (Stadt) session.createCriteria(City.class).add(Restrictions.eq("name", "Nijmegen")).uniqueResult();
    stad.getPostalCode(new Straße("KronenburgerSingel")).code = "6511 AP";
    transaction.commit();

    wird nicht zu einer Aktualisierung der Datenbank führen. Wenn die Klasse PostalCode in eine Entität umgewandelt würde, wäre Hibernate in der Lage, die Änderungen zu erkennen und die Aktualisierung durchzuführen.

Verfasst von

Maarten Winkels

Contact

Let’s discuss how we can support your journey.