在Hibernate验证期间执行EntityManager查询的正确方法

我是一个 Java EE / EJB noob,但是从我收集的文档和其他帖子中,您无法在实体验证期间使用相同的entitymanager / session查询数据库.

In general, the lifecycle method of a portable application should not invoke EntityManager
or Query operations, access other entity instances, or modify relationships within the
same persistence context.[43] A lifecycle callback method may modify the non-relationship
state of the entity on which it is invoked.

翻译好吗?

这是非常抽象的……可以用更具体的术语来解释吗?它导致的问题多于答案.例如,如果我的实体有一个延迟加载的集合,我可以在验证期间访问它吗?该集合是“另一个实体”,需要数据库查询,这似乎违反了文档.

这种“生命周期要求”似乎很奇怪,因为某些验证确实需要查询数据库才是生活中的事实.

从其他帖子我也看到人们通过使用entitymanagerfactory创建一个新的entitymanager / session来解决这个查询问题.

这引出了两个关于使用EntityManagers和Hibernate验证的问题:

>我是否有可能遇到某种设计缺陷或滥用Hibernate验证,因为我需要在验证期间查询数据库?
>鉴于我在JBoss中使用Java EE,如何使用EntityManagerFactory注入验证器?

我尝试过这样的事情:

@Stateless
public class UserValidator implements ConstraintValidator<ValidUser, User> {
    @PersistenceUnit(unitName="blahblah")
    EntityManagerFactory emf;

    ...
}

但EMF永远不会注入.我猜@Stateless标签变得无关紧要,因为我正在实现一个ConstraintValidator接口,这是Hibernate Validator工作所需要的.

那么从Validator获取EntityManagerFactory的一般模式是什么?

谢谢!

通过一些评论和足够的搜索,我终于想出了一个有点“规范”的方式来回答我的问题.

但为了清理问题,我的问题实际上是要求两个有两个不同答案的东西:

>如何将内容注入Hibernate Validation框架中使用的Validators?
>假设我们可以注入东西,注入EntityManagerFactory或EntityManager并在JPA生命周期事件期间使用它们进行查询是否安全?

首先回答第二个问题,我简单地说,强烈建议在验证期间使用第二个EntityManager进行查询.这意味着您应该注入一个EntityManagerFactory并为查询创建一个新的EntityManager(而不是注入一个EntityManager,它将与创建生命周期事件的EntityManager相同).

一般来说,出于验证目的,您只需要查询数据库而不是插入/更新,因此这应该是相当安全的.

我问了一个非常相关的SO问题here.

现在回答问题1.

是的,完全有可能将东西注入到Hibernate Validation框架中使用的Validators中.要做到这一点,你需要做3件事:

>创建一个自定义ConstraintValidatorFactory,它将创建框架中使用的验证器(覆盖Hibernate的默认工厂). (我的例子使用Java EE,而不是Spring,所以我使用BeanManager,但在Spring中你可能会使用ApplicationContext).
>创建一个validation.xml文件,该文件告诉Hibernate Validation框架将哪个类用于ConstraintValidatorFactory.确保此文件最终在您的类路径上.
>写一个注入东西的验证器.

以下是使用“托管”(可注入)验证器的自定义ConstraintValidatorFactory示例:

package com.myvalidator;

public class ConstraintInjectableValidatorFactory implements ConstraintValidatorFactory {

    private static BeanManager beanManager;

    @SuppressWarnings(value="unchecked")
    @Override
    public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> clazz) {
        // lazily initialize the beanManager
        if (beanManager == null) {
            try {
                beanManager = (BeanManager) InitialContext.doLookup("java:comp/BeanManager");
            } catch (NamingException e) {
                // TODO what's the best way to handle this?
                throw new RuntimeException(e);
            }
        }

        T result = null;

        Bean<T> bean = (Bean<T>) beanManager.resolve(beanManager.getBeans(clazz));
        // if the bean/validator specified by clazz is not null that means it has
        // injection points so get it from the beanManager and return it. The validator
        // that comes from the beanManager will already be injected.
        if (bean != null) {
            CreationalContext<T> context = beanManager.createCreationalContext(bean);
            if (context != null) {
                result = (T) beanManager.getReference(bean, clazz, context);
            }
        // the bean/validator was not in the beanManager meaning it has no injection
        // points so go ahead and just instantiate a new instance and return it
        } else {
            try {
                result = clazz.newInstance();
            } catch (Throwable t) {
                throw new RuntimeException(t);
            }
        }

        return result;
    }
}

这是一个示例validation.xml文件,它告诉Hibernate Validator哪个类用作ValidatorFactory:

<?xml version="1.0" encoding="UTF-8"?>
<validation-config
    xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration validation-configuration-1.0.xsd">
    <constraint-validator-factory>
        com.myvalidator.ConstraintInjectableValidatorFactory
    </constraint-validator-factory>
</validation-config>

最后是一个带注入点的验证器类:

public class UserValidator implements ConstraintValidator<ValidUser, User> {

    @PersistenceUnit(unitName="myvalidator")
    private EntityManagerFactory entityManagerFactory;

    private EntityManager entityManager;

    @Override
    public void initialize(ValidUser annotation) {
    }

    @Override
    public boolean isValid(User user, ConstraintValidatorContext context) {
        // validation takes place during the entityManager.persist() lifecycle, so
        // here we create a new entityManager separate from the original one that
        // invoked this validation
        entityManager = entityManagerFactory.createEntityManager();

        // use entityManager to query database for needed validation

        entityManager.close();
    }
}
相关文章
相关标签/搜索