Ich habe eine Entität, auf der ich die folgende Einschränkung implementieren muss: "Es kann immer nur einen Datensatz für jede Kombination von Spalten X und Y geben, für die Spalte Null ist, wenn der Datensatz vom Typ A ist."Kann ich mit der Hibernate-Validierung die zu validierende Entität abfragen?
Der letzte Teil verwandelt dies von einer einfachen Eindeutigkeitseinschränkung in etwas Komplexeres. Ich schreibe einen benutzerdefinierten Hibernate-Validator, um es zu überprüfen.
Was ich tue, ist:
@Override
public boolean isValid(MyEntity value, ConstraintValidatorContext context) {
Query query = DB.createQuery(// DB is just a convenience class
"select count(*) from MyEntity" +
" where propertyX = :propertyX" +
" and propertyY = :propertyY" +
" and type = :type" +
" and propertyZ is null")
.setParameter("propertyX", value.getPropertyZ())
.setParameter("propertyY", value.getPropertyY())
.setParameter("type", MyType.PRIMARY);
return query.getResultList().size() <= 1;
}
Wenn es mehr als einen solchen Datensatz sollte die Validierung fehl. Dadurch wird immer die Einstellung propertyZ
erzwungen, bevor ein neuer Eintrag eingefügt wird.
jedoch dies nicht funktioniert, weil diese Validierung onPersist
und an diesem Punkt ist nun mal die Abfrage gibt ein Ergebnis mit einer null
id, die eine Ausnahme verursacht.
Hier sind einige interessanten Zeilen aus dem Stack-Trace:
[junit] org.hibernate.AssertionFailure: null id in my.package.MyEntity entry (dont flush the Session after an exception occurs)
[junit] at org.hibernate.event.def.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:82)
[junit] at org.hibernate.event.def.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:190)
[junit] at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:147)
[junit] at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:219)
[junit] at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99)
[junit] at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:58)
[junit] at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:1185)
[junit] at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1261)
[junit] at org.hibernate.impl.QueryImpl.list(QueryImpl.java:102)
[junit] at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:246)
[junit] at my.package.validation.UniqueCombinationTypeValidator.isValid(UniqueCombinationTypeValidator.java:42)
[junit] at my.package.validation.UniqueCombinationTypeValidator.isValid(UniqueCombinationTypeValidator.java:14)
[junit] at org.hibernate.validator.engine.ConstraintTree.validateSingleConstraint(ConstraintTree.java:153)
[junit] at org.hibernate.validator.engine.ConstraintTree.validateConstraints(ConstraintTree.java:140)
...
[junit] at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61)
[junit] at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:808)
[junit] at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:782)
[junit] at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:786)
[junit] at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:672)
[junit] at my.package.DB.persist(DB.java:278)
[junit] at my.package.test.model.validation.UniqueCombinationTypeValidatorTest.testInsert(UniqueCombinationTypeValidatorTest.java:72)
Eine andere Sache zu beachten ist, dass die Tabelle leer ist, wenn dieser erste Einsatz erfolgt.
DIE FRAGE IST, kann ich die gleiche Tabelle abfragen, die ich zu validieren versuche? Es scheint eine sehr logische Anforderung zu sein, da eine Constraint-Annotation in die Klasse eingefügt werden kann. Meine Validierung hängt vom Zustand der Daten ab.
Ich frage mich, ob es die neue Abfrage-Lookup innerhalb der Transaktion für die Persistenz tut? –
Aus dem Ruhezustand [Forum] (https://forum.hibernate.org/viewtopic.php?f=1&t=1004374): "Es ist nicht in Ordnung, dieselbe Sitzung in Callback-Methoden, die von der Session." Dies ist wahrscheinlich das Problem. –