通过一些评论和足够的搜索,我终于找到了一种有点“规范”的方式来回答我的问题。
但为了澄清问题,我的问题实际上是问两件事,它们有两个不同的答案:
- 你如何将东西注入到 Hibernate Validation 框架中使用的 Validator 中?
- 假设我们可以注入东西,注入 EntityManagerFactory 或 EntityManager 并在 JPA 生命周期事件期间使用它们进行查询是否安全?
首先回答第二个问题我会简单地说,强烈建议在验证期间使用第二个 EntityManager 进行查询。这意味着您应该注入一个 EntityManagerFactory 并为查询创建一个新的 EntityManager(而不是注入一个与创建生命周期事件相同的 EntityManager)。
一般来说,出于验证目的,您无论如何都只会查询数据库而不是插入/更新,所以这应该是相当安全的。
我在这里问了一个非常相关的 SO 问题。
现在回答问题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();
}
}