Michael HönnigMichael Hönnig
EclipseLink macht es richtig aber sinnbefreit und Hibernate macht es falsch aber sinnvoll, so könnte man das Verhalten dieser beiden JPA Provider beschreiben, wenn es um Bean Validation geht. Nach der JSR 317, Java (TM) Persistence API, Version 2.0 final vom 10. November 2009, kann die automatisch Bean Validation in den Events pre-persist, pre-update und pre-remove stattfinden, also in EntityManager.persist() für pre-persist, EntityManager.flush() für pre-update und EntityManager.remove() für pre-remove. Bereits hier ist eine Assymmetrie zu erkennen, denn während vorab existierende und veränderte Entitäten am Anfang von EntityManager.flush() validiert werden, also unmittelbar bevor sie in die Datenbank-Transaktion geschrieben werden. werden neue Entitäten bereits in EntityManager.persist() validiert statt erst kurz bevor sie in die Datenbank-Transaktion geschrieben werden. So ist es zumindest in der JPA Spezifikation beschrieben. Nach JSR 317, Seite 100:
"Automatic validation using these constraints is achieved by specifying that Java Persistence delegate validation to the Bean Validation implementation upon the pre-persist, pre-update, and pre-remove entity lifecycle events described in Section 3.5.2."
Erstaunlich ist auch, dass JPA 2.0 kein pre-insert Event kennt, sondern pre-update auch für neue Entitäten gilt. Hier warnt die Spezifikation daher auch vor implementationsabhängigen Verhalten, dass nämlich neue Entitäten durchaus auch noch im flush, nämlich beim pre-update validiert werden können, aber nicht validiert werden müssen. So schreibt JSR 317, Seite 101:
"In the case where an entity is persisted and subsequently modified in a single transaction or when an entity is modified and subsequently removed in a single transaction, it is implementation dependent as to whether the pre-update validation event occurs. Portable applications should not rely on this behavior."
EclipseLink in der getesteten Version 2.4.2-RC1 folgt auch völlig der Spezifikation, indem es die Validierung neuer Entitäten nur in EntityManager.persist() durchführt, nicht aber im optionalen Entitymanager.flush(). Dies kann dazu führen, dass invalide Änderungen an neuen Entitäten, die zwischen persist und flush durchgeführt werden, in die Datenbank geschrieben werden. Constraints auf der Datenbank können selbstverständlich korrupte Daten verhindern, aber nicht alle Bean Validierungen können sinnvoll auch als Datenbank-Constraints implementiert werden - zudem sind die Fehlermeldungen schwieriger bis gar nicht in Benutzer-lesbarer Form darstellbar. Hibernate in der getesteten Version 4.2.1-Final dahingegen bricht mit der Spezifikation, als dass es im EntityManager.persist() keine Validerung durchführt. Allerdings validiert es auch neue Entitäten in EntitiyManager.flush() und verhindert somit invalide Daten an der Datenbank weiter zu reichen. Auch wenn Hibernate hier gegen die Spezifikation verstößt, halte ich das Verhalten von Hibernate für sinnvoller. Sowohl im pre-persist als auch im pre-update zu validieren hat schließlich auch Performance-Nachteile. Im Anhang findet sich ein Maven-Projekt mit dem das hier beschriebene Verhalten nachvollzogen werden kann: beanvalidation-test