Hibernate Envers: Suche in Collections

07.05.2014

Hibernate EnversIn vielen Softwareprojekten fällt die Wahl bei der Java Persistence API (JPA)-Implemetierung auf Hibernate. Wie schon im Beitrag „Auditing mit Hibernate Envers“ beschrieben, kann man mit Hibernate Envers Änderungen am Datenbestand speichern und abfragen. Die Abfrage der Audit-Tabellen erfolgt analog der üblichen Hibernate Queries und die Syntax ist sehr ähnlich.

In einem Projekt ist kürzlich bei der Suche in den Änderungen folgende Fehlermeldung aufgetreten:
org.hibernate.envers.exception.AuditException:
  This criterion cannot be used on a property that is a relation to another property.

Die Fehleranalyse

Was war passiert? Beide Klassen wurden erzeugt und mit @Audited annotiert. Wie man sieht, gibt es eine Verbindung zwischen den Klassen, denn eine „Entenmutter“ hat mehrere „Entenküken“.

@Entity
@Audited
@Table(name = "duck")
public class Duck {
  ...
  @OneToMany(mappedBy = "mother")
  Set ducklings = new HashSet();
  ...
}

@Entity
@Audited
@Table(name = "duckling")
public class Duckling {
  ...
  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "duck_id", nullable = true, insertable = true, updatable = true)
  private Duck mother;
  ...
}

Für die Anzeige der Daten wurde ein Filter verwendet, der auf verschiedene Eigenschaften filtern kann; z.B. können alle „Entenküken“ gefunden werden, deren Namen mit „La“ beginnen (das würde die Suche in Zeile 2 erledigen). Der Filter kann auch mit einer Liste von Parametern arbeiten, wenn man z.B. alle Entenküken der Enten-Mütter „Martha, Berta und Daisy“ sehen möchte (Zeile 4).
Der Filter sieht dann so aus:

if (parameterValue instanceof String) {
  auditQuery.add(AuditEntity.property(parameterName).like((String) parameterValue, MatchMode.ANYWHERE));
} else if (parameterValue instanceof Set) {
  auditQuery.add(AuditEntity.property(parameterName).in((Set) parameterValue));
} else {
  auditQuery.add(AuditEntity.property(parameterName).eq(parameterValue));
}

Das Problem ist die Anweisung „property(parameterName).in“ in Zeile 4. Anscheinend kann Hibernate Envers (in Version 4.1.2.Final) dies nicht richtig auflösen. Es ist zwar gültiger Code und die JavaDoc gibt auch keinen Hinweis, warum dies nicht erlaubt sein sollte, aber genau an dieser Stelle wird die oben angegebene Exception geworfen.

Die Problemlösung

Die Lösung für das Problem war, die „in“ Anweisung durch geschachtelte „or“ Anweisungen zu ersetzen. Also z.B. so:

...
auditQuery.add(createAuditCriterionForSet(parameterName, (Set) parameterValue));
...

und einer Methode, die die „or“ Anweisung umsetzt

private static AuditCriterion createAuditCriterionForSet(String parameterName, Set parameterValue) {
  AuditCriterion auditCriterion = null;
  for (Object object : parameterValue) {
    if (auditCriterion == null) {
      auditCriterion = AuditEntity.property(parameterName).eq(object);
    } else {
      auditCriterion = AuditEntity.or(auditCriterion, AuditEntity.property(parameterName).eq(object));
    }
  }
  return auditCriterion;
}

Es wäre natürlich schöner, wenn der ursprüngliche Code funktionieren würde. Solange Hibernate diese Stelle nicht repariert, ist dieser Workaround jedoch eine Möglichkeit, die gewünschte Suche durchzuführen.

Zurück zur Übersicht

Ein Kommentar zur “Hibernate Envers: Suche in Collections

Kommentar verfassen

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

*Pflichtfelder

*