Bean Validation ist genial – zumindest meistens: Instanzvariablen oder Property-Getter von Domänenobjekten können einfach mittels Annotationen mit Regeln – Constraints – versehen werden, um bspw. auszudrücken, dass ein Feld nicht leer bleiben darf (@NotNull String name
) oder bestimmte Wertebereiche nicht verlassen darf (@Min(18) int age
).
Geprüft werden die Constraints dann z. B. in JSF-Eingabeformularen oder beim Speichern von JPA-Entities. Zudem ist eine explizite Validierung per API möglich. Da bleiben eigentlich kaum Wünsche offen, wenn da nicht eine recht eigenwillige – man könnte auch sagen “unsinnige” – Regelung in der JPA-Spezifikation zum Grübeln anregen würde:
Die JPA-Spezifikation verlangt von JPA-Providern, dass sie die Validität von in der Datenbank einzutragenden Objekten vor der Speicherung überprüfen, und zwar “upon the
pre-persist, pre-update, and pre-remove lifecycle … events” (Abschnitt 3.6.1 der Spec.).
Die Referenzimplementierung EclipseLink hält sich da auch sklavisch dran, was aber im Falle der Neuanlage eines DB-Eintrags einigermaßen sinnbefreit ist: Wird ein transientes Objekt an EntityManager.persist
übergeben, geschieht die Prüfung in diesem Moment und ungültige Objekte werden abgelehnt (via ConstraintViolationException
). Ändert man das Objekt in der Folge, aber noch vor dem Commit, wird nicht etwa nochmals geprüft! Dadurch können also (hoffentlich unbewusst) ungültige Daten in die DB gelangen. Auf der sicheren Seite ist man nur, wenn man vor dem Commit explizit EntityManager.flush
aufruft. Dann wird nämlich erneut geprüft …
Es kommt aber noch schlimmer: Bean Validation erlaubt auch Constraints auf Properties, die nicht Teil der persistenten View eines JPA-Objektes sind. So könnte z. B. für ein Objekt mit Field Access – bei dem also die Instanzvariablen auf DB-Spalten gemapped werden – eine Methode namens isValid
mit Bean Validation Constraintzs versehen sein, um bspw. feldübergreifende Gültigkeitsregeln zu prüfen. EclipseLink ruft diese Validierunsmethode nur dann auf, wenn es zumindest ein BV Constraint auf einem persistenten Feld gibt. Es macht den Eindruck, als hätte da jemand ein bisschen über-optimiert …
Hibernate auf der anderen Seite verhält sich absolut sinnvoll: BV Constraints werden immer vor dem eigentlichen DB-Eintrag geprüft. Das entspricht zwar nicht den Worten der Spezifikation, verhindert aber ungültige Einträge in der DB, was ja wohl der Sinn und Zweck der Angelegenheit ist.
Wenn Sie’s ausprobieren wollen: Unter https://github.com/GEDOPLAN/jpa-bv-demo
finden Sie ein Demo-Projekt, das mittels Maven-Profilen zwischen EclipseLink und Hibernate umgeschaltet werden kann.
2 Kommentare. Hinterlasse eine Antwort
Hallo,
vielen Dank für diesen interessanten Blogeintrag.
Ich würde diesen Blog gern über den RSS-Feed lesen, aber leider ist er veraltet und die neuen Blogeinträge tauchen dort nicht mehr auf. Mir scheint die letzte Aktualisierung ist am
Tue, 15 Dec 2015 10:09:47 +0000 erfolgt.
Besten Gruß,
Tobias
Hallo Tobias,
das war ein Problem in unserer WordPress-Konfiguration. Es sollte jetzt wieder funktionieren.
Viele Grüße
Dirk