Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/hibernate/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Hibernate验证期间执行EntityManager查询的正确方法_Hibernate_Jakarta Ee_Entitymanager_Hibernate Validator - Fatal编程技术网

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

在Hibernate验证期间执行EntityManager查询的正确方法,hibernate,jakarta-ee,entitymanager,hibernate-validator,Hibernate,Jakarta Ee,Entitymanager,Hibernate Validator,我有点像JavaEE/EJBNoob,但从我收集的文档和其他帖子来看,在实体验证期间,您无法使用相同的entitymanager/session查询数据库 通常,可移植应用程序的生命周期方法不应调用EntityManager 或查询操作,访问其他实体实例,或修改 相同的持久性上下文。[43]生命周期回调方法可能会修改非关系 对其调用的实体的状态 请翻译一下好吗 这是相当抽象的…能用更具体的术语来解释吗?它会引出更多的问题,而不是答案。例如,如果我的实体有一个延迟加载的集合,是否允许我在验证期间访

我有点像JavaEE/EJBNoob,但从我收集的文档和其他帖子来看,在实体验证期间,您无法使用相同的entitymanager/session查询数据库

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

请翻译一下好吗

这是相当抽象的…能用更具体的术语来解释吗?它会引出更多的问题,而不是答案。例如,如果我的实体有一个延迟加载的集合,是否允许我在验证期间访问它?该集合是“另一个实体”,需要DB查询,这似乎违反了文档

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

在其他帖子中,我还看到人们通过使用entitymanagerfactory创建一个新的entitymanager/会话来解决这个查询问题

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

  • 我是否可能有某种设计缺陷,或者因为需要在验证期间查询数据库而误用了Hibernate验证
  • 考虑到我在JBoss中使用JavaEE,如何将验证器注入EntityManagerFactory
  • 我试过这样的方法:

    @Stateless
    public class UserValidator implements ConstraintValidator<ValidUser, User> {
        @PersistenceUnit(unitName="blahblah")
        EntityManagerFactory emf;
    
        ...
    }
    
    @无状态
    公共类UserValidator实现ConstraintValidator{
    @PersistenceUnit(unitName=“blahblah”)
    实体管理工厂emf;
    ...
    }
    
    但是电动势永远不会被注入。我猜@Stateless标记变得无关紧要,因为我正在实现一个ConstraintValidator接口,这是Hibernate验证器工作所必需的

    那么,从验证器获取EntityManagerFactory的一般模式是什么


    谢谢

    我想我理解您的愿望,希望使用Aweasome bean validation API进行所有验证,但请记住这不是必需的

    此外,考虑这两个要求:

  • 密码不能为空
  • 用户不得使用与以前任何密码相同的密码
  • 第一个显然只取决于密码本身,我将其归类为验证数据,因此这种验证属于数据层

    第二个取决于一段数据与许多其他实体的关系,或与系统的当前状态的关系。我会将其归类为属于业务层的内容

    也就是说,不要试图将验证约束放在实体类上,而是将它们放在一些业务层类上(是的,如果您现在愿意,您甚至可以使用bean验证)

    例如,假设您有一个带有当前密码字段的
    用户
    实体和一个
    密码
    实体,您可以从中查询用户的旧密码。现在使您的用户数据访问对象:

    @Stateful // yes stateful, need the same instance across method invocations
    @ValidatePassword
    public class UserDao {
    
        @PersistenceContext private EntityManager em;
        private String password;
    
        public String getPassword() {
            return this.password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public boolean isValidPassword() {
            // use the em to find the old passwords
            // check that the submitted password is valid
        }
    
        public void savePassword() {
            // find the user
            // set the user's now valid password
        }
    }
    
    创建类级别约束:

    @Target( { TYPE })
    @Retention(RUNTIME)
    @Constraint(validatedBy = MyPasswordValidator.class)
    public @interface ValidatePassword {
    
        String message() default "error message";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    
    }
    
    @Target({TYPE})
    @保留(运行时)
    @约束(validatedBy=MyPasswordValidator.class)
    public@interface ValidatePassword{
    字符串消息()默认为“错误消息”;
    类[]组()默认值{};
    等级
    请翻译一下好吗

    生命周期事件不应使用实体管理器,因为它可能导致回归。假设在预更新事件期间,您修改了另一个实体。这将在以前的预更新事件中生成另一个预更新事件。为避免此类问题,不鼓励使用实体管理器

    但是,如果您只想读取一些额外的数据,从概念上讲,这并没有问题。值化在预更新和预插入事件中隐式发生

    如果从不使用加载后事件,则读取生命周期事件中的数据不应触发嵌套的生命周期事件。据我所知,规范并不严格禁止查询实体,但强烈反对查询实体。在这种情况下,这可能没问题。您尝试过这样做吗

    那么,进入EntityManager工厂的一般模式是什么 来自验证器


    注入仅在托管实体中工作。当注入不可能时,您应该能够执行良好的旧操作。但是,当使用第二个实体管理器时,可能会生成嵌套的生命周期事件。但是,如果您只执行一些琐碎的操作,如读取旧密码列表,则应该可以。

    通过一些注释和足够的CRO经过反复思考,我终于想出了一个有点“规范”的方法来回答我的问题

    但为了澄清问题,我的问题实际上是问了两件事,它们有两个截然不同的答案:

  • 如何将东西注入到Hibernate验证框架中使用的验证器中
  • 假设我们可以注入东西,那么注入EntityManagerFactory或EntityManager并在JPA生命周期事件期间使用它们进行查询是否安全
  • 首先回答第二个问题,我只想说,强烈建议在验证期间使用第二个EntityManager进行查询。这意味着您应该注入EntityManager工厂,并为查询创建新的EntityManager(而不是注入一个EntityManager,该EntityManager将与最初创建生命周期事件的EntityManager相同)

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

    我问了一个非常相关的问题

    现在回答问题1

    是的,完全可以将内容注入到Hibernate验证框架中使用的验证器中。要实现这一点,您需要做3件事:

  • 创建自定义ConstraintValidatorFactory,该工厂将创建框架中使用的验证器
    public class MyPasswordValidator implements ConstraintValidator<ValidatePassword, UserDao> {
    
        public void initialize(SelfValidating constraintAnnotation) {
            // get message, etc.
        }
    
        public boolean isValid(UserDao userDao, ConstraintValidatorContext constraintValidatorContext) {
            return userDao.isValidPassword();
        }
    }
    
    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;
        }
    }
    
    <?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();
        }
    }