在Hibernate验证期间执行EntityManager查询的正确方法
我有点像JavaEE/EJBNoob,但从我收集的文档和其他帖子来看,在实体验证期间,您无法使用相同的entitymanager/session查询数据库 通常,可移植应用程序的生命周期方法不应调用EntityManager 或查询操作,访问其他实体实例,或修改 相同的持久性上下文。[43]生命周期回调方法可能会修改非关系 对其调用的实体的状态 请翻译一下好吗 这是相当抽象的…能用更具体的术语来解释吗?它会引出更多的问题,而不是答案。例如,如果我的实体有一个延迟加载的集合,是否允许我在验证期间访问它?该集合是“另一个实体”,需要DB查询,这似乎违反了文档 这种“生命周期需求”似乎很奇怪,因为某些验证确实需要查询数据库,这是一个事实 在其他帖子中,我还看到人们通过使用entitymanagerfactory创建一个新的entitymanager/会话来解决这个查询问题 这就引出了有关使用EntityManager和Hibernate验证的两个问题:在Hibernate验证期间执行EntityManager查询的正确方法,hibernate,jakarta-ee,entitymanager,hibernate-validator,Hibernate,Jakarta Ee,Entitymanager,Hibernate Validator,我有点像JavaEE/EJBNoob,但从我收集的文档和其他帖子来看,在实体验证期间,您无法使用相同的entitymanager/session查询数据库 通常,可移植应用程序的生命周期方法不应调用EntityManager 或查询操作,访问其他实体实例,或修改 相同的持久性上下文。[43]生命周期回调方法可能会修改非关系 对其调用的实体的状态 请翻译一下好吗 这是相当抽象的…能用更具体的术语来解释吗?它会引出更多的问题,而不是答案。例如,如果我的实体有一个延迟加载的集合,是否允许我在验证期间访
@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进行所有验证,但请记住这不是必需的 此外,考虑这两个要求:
用户
实体和一个密码
实体,您可以从中查询用户的旧密码。现在使您的用户数据访问对象:
@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();
}
}