Могу ли я запросить проверяемый объект с помощью проверки Hibernate? - PullRequest
3 голосов
/ 28 марта 2012

У меня есть объект, для которого мне нужно реализовать следующее ограничение: "Для любой комбинации столбцов X и Y может быть только одна запись, для которой столбец Z равен нулю, если запись имеет тип A."

Последняя часть превращает это из простого ограничения уникальности в нечто более сложное. Я пишу пользовательский валидатор Hibernate, чтобы проверить его.

Что я делаю, это:

@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;
}

Если существует более одной такой записи, проверка должна завершиться неудачно. Это будет принудительно всегда устанавливать propertyZ перед вставкой новой записи.

Однако это не работает, потому что эта проверка происходит onPersist, и в этот момент запрос возвращает результат с идентификатором null, что вызывает исключение.

Вот несколько интересных строк из трассировки стека:

[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)

Еще одна вещь, на которую следует обратить внимание, это то, что таблица пуста после первой вставки.

ВОПРОС ЕСТЬ, можно ли запросить ту же таблицу, которую я пытаюсь проверить? Это кажется очень логичным требованием, так как аннотацию ограничения можно поместить в класс. Моя проверка зависит от состояния данных.

1 Ответ

3 голосов
/ 29 марта 2012

Вдохновленный этим постом и знанием того, что никогда не нормально вызывать / использовать один и тот же сеанс в любых методах обратного вызова, которые запускаются из сеанса, мне удалось решить проблема довольно проста, если взять мой EntityManagerFactory, что, в свою очередь, позволяет мне получить EntityManager, что дает мне новый Session. Теперь я могу использовать это для выполнения запроса.

EntityManager em = emFactory.createEntityManager();
session = (Session) em.getDelegate();
Query query = session.createQuery(...

ПРИМЕЧАНИЕ: То, что из EntityManager docs , getDelegate(), зависит от конкретной реализации. Я использую tomcat и hibernate, и это хорошо работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...