在Hibernate Validation期间纠正一个EntityManager查询的方法 [英] Correct way to do an EntityManager query during Hibernate Validation

查看:110
本文介绍了在Hibernate Validation期间纠正一个EntityManager查询的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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


通常,可移植应用程序的生命周期方法不应调用EntityManager
或Query操作,访问其他实体实例或修改
内的关系相同的持久性上下文。[43]生命周期回调方法可能会修改调用它的实体的非关系
状态。


请翻译吗?

这非常抽象......可以用更具体的术语来解释它吗?它会导致更多的问题而不是它的答案。例如,如果我的实体有一个延迟加载的集合,我允许在验证期间访问它吗?该集合是'另一个实体',并且需要一个似乎违反文档的DB查询。



这个生命周期需求看起来很奇怪,因为它只是一个事实一些验证确实需要查询数据库。



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



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


  1. 是否有可能我有某种设计缺陷或者滥用Hibernate Validation,因为我需要在验证过程中查询数据库? 考虑到我正在使用Java EE和JBoss,我是否使用EntityManagerFactory注入我的验证器?

我试过这样的事情:

  @Stateless 
public class UserValidator implements ConstraintValidator< ValidUser,使用者名称> {
@PersistenceUnit(unitName =blahblah)
EntityManagerFactory emf;

...
}

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



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



谢谢!

解决方案

通过一些评论和足够的评论,我终于想出了一个有点经典的方式来回答我的问题。



但是为了澄清事情,我的问题确实是问两个有两个不同答案的东西:


  1. 你如何在Hibernate验证框架中使用的验证器中注入东西?
  2. >
  3. 假设我们可以注入事物,注入一个EntityManagerFactory或EntityManager并在JPA生命周期事件中使用它们进行查询是安全的吗?

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



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



我问了一个非常相关所以这里的问题



现在回答问题1。



是的,完全可以在Hibernate验证框架中使用的验证器中注入东西。要做到这一点,你需要做3件事:


  1. 创建一个自定义ConstraintValidatorFactory,它将创建框架中使用的验证器(覆盖Hibernate的默认工厂)。 (我的例子使用Java EE,而不是Spring,所以我使用BeanManager,但在Spring中,您可能会使用ApplicationContext来实现此目的)。 创建一个validation.xml文件,告知Hibernate验证框架哪个类用于ConstraintValidatorFactory。确保这个文件在你的类路径中结束。

  2. 编写一个验证器来注入一些东西。
  3. >这是一个使用'managed'(注入)验证器的自定义ConstraintValidatorFactory示例:

      package com.myvalidator; 

    public class ConstraintInjectableValidatorFactory implements ConstraintValidatorFactory {

    private static BeanManager beanManager;

    @SuppressWarnings(value =unchecked)
    @Override
    public< T extends ConstraintValidator<?,?>>>如果(beanManager == null){
    try {
    beanManager =(BeanManager)InitialContext.doLookup(){getBinding(Class< T> clazz){
    // lazily初始化beanManager
    ( Java的:COMP / BeanManager);
    } catch(NamingException e){
    // TODO处理这个问题的最好方法是什么?
    抛出新的RuntimeException(e);
    }
    }

    T result = null;

    Bean< T> bean =(Bean< T>)beanManager.resolve(beanManager.getBeans(clazz));
    //如果由clazz指定的bean /验证器不为null,那意味着它有
    //注入点,所以从beanManager中获取并返回它。来自beanManager的验证器
    //将被注入。
    if(bean!= null){
    CreationalContext< T> context = beanManager.createCreationalContext(bean);
    if(context!= null){
    result =(T)beanManager.getReference(bean,clazz,context);
    }
    // bean / validator不在beanManager中,这意味着它没有注入
    //点,所以继续,只是实例化一个新实例并返回它
    } else {
    尝试{
    result = clazz.newInstance();
    } catch(Throwable t){
    throw new RuntimeException(t);
    }
    }

    返回结果;




    $ b

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

     <?xml version =1.0encoding =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;

    私人EntityManager entityManager;

    @Override
    public void initialize(ValidUser annotation){
    }

    @Override
    public boolean isValid(User user,ConstraintValidatorContext context ){
    //验证发生在entityManager.persist()生命周期中,所以
    //在这里我们创建一个新的entityManager,与原始的
    //调用此验证
    entityManager = entityManagerFactory.createEntityManager();

    //使用entityManager查询需要验证的数据库

    entityManager.close();
    }
    }


    I'm a bit of a Java EE/EJB noob, but from the docs and other posts I've gathered you cannot query the database using the same entitymanager/session during entity validation.

    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.

    Translation please?

    This is pretty abstract...can it be explained in more concrete terms? It leads to more questions than it answers. For example, if my entity has a lazy-loaded collection am I allowed to access it during validation? The collection is 'another entity' and will require a DB query which seems to be in violation of the docs.

    This 'lifecycle requirement' seems odd because it's just a fact of life that certain validations do indeed require querying the database.

    From other posts I've also seen people get around this querying issue by creating a new entitymanager/session using the entitymanagerfactory.

    This leads me to two questions about using EntityManagers and Hibernate Validation:

    1. Is it possible I have some sort of design flaw or am misusing Hibernate Validation because I need to query the database during validation?
    2. Given that I'm using Java EE with JBoss, how do I inject my validator with an EntityManagerFactory?

    I've tried something like this:

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

    But the EMF never gets injected. I'm guessing the @Stateless tag becomes irrelevant because I'm implementing a ConstraintValidator interface which is needed for the Hibernate Validator stuff to work.

    So what's the general pattern for getting at an EntityManagerFactory from a Validator?

    Thanks!

    解决方案

    Through some of the comments and enough scrounging around, I finally figured out a somewhat 'canonical' way to answer my question.

    But to clear things up, my question really was asking two things which have 2 distinct answers:

    1. How do you inject things into Validators that are used in the Hibernate Validation framework?
    2. Assuming we can inject things is it safe to inject an EntityManagerFactory or EntityManager and use them for querying during a JPA lifecycle event?

    Answering the second question first I'll simply say, it is strongly encouraged to use a second EntityManager to do queries during validation. That means you should be injecting an EntityManagerFactory and creating a new EntityManager for queries (rather than injecting an EntityManager which will be the same one that created the lifecycle event to begin with).

    Generally speaking, for validation purposes, you'll only be querying the database anyway and not inserting/updating so this should be fairly safe to do.

    I asked a very related SO question here.

    Now to answer question 1.

    Yes it is completely possible to inject things into Validators used in the Hibernate Validation framework. To accomplish this you need to do 3 things:

    1. Create a custom ConstraintValidatorFactory that will create the validators used in the framework (overriding Hibernate's default factory). (My example uses Java EE, not Spring so I use BeanManager, but in Spring you'd probably use ApplicationContext for this).
    2. Create a validation.xml file which tells the Hibernate Validation framework which class to use for the ConstraintValidatorFactory. Make sure this file ends up on your classpath.
    3. Write a Validator that injects something.

    Here is an example custom ConstraintValidatorFactory that uses 'managed' (injectable) validators:

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

    Here is an example validation.xml file that tells Hibernate Validator which class to use as the 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>
    

    And finally a validator class with injection points:

    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();
        }
    }
    

    这篇关于在Hibernate Validation期间纠正一个EntityManager查询的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆