我一直在谷歌搜索,我已经没时间了,所以我把它扔到这里,希望有人知道答案。我有一个系统,我在服务层有弹簧事务边界。下面是一个dao层。我已经在我的模型对象上进行了bean验证,并且我已经在编译时方面中包装了DAO。围绕这样的方面:
@Aspect
public class ValidationCollectorAspect {
@Around("daoMethods()")
public Object collectDaoMessages(ProceedingJoinPoint thisJoinPoint) throws Throwable {
try {
return thisJoinPoint.proceed();
} catch (ConstraintViolationException e) {
Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
List<UserMessage> userMessages = ThreadContext.MESSAGES.get();
for (ConstraintViolation<?> constraintViolation : constraintViolations) {
userMessages.add(new UserMessage(constraintViolation.getMessage(), MessageType.VALIDATION));
}
throw new MyPersistenceException("Validation failures", e);
}
}
@Pointcut("call(public * *(..)) &&!call(* getEntityManager()) && within(com.myclient.dao.impl..*)")
public void daoMethods() {
}
}
问题是验证似乎是在提交事务时发生的,而不是在DAO中的保存或更新操作之前。这意味着在服务方法返回之后并且在此连接点之后,不会抛出来自bean验证的ConstraintViolationException。我的证据是stack trace不包含任何dao服务方法。我编写的第一个代码方法由
显示 at com.myclient.servlet.rest.Rest.updateObjects(Rest.java:323)
但这是一个关于servlet名称Rest的方法,而且重点是不需要为系统中各种servlet上的一大堆特定方法创建连接点,并且还能够处理Constraint违规在它包含在弹簧异常的任意层之前。
据我所知,有时在提交之前验证所有hibernate更改的总和可能很酷,但这不是我想要的。 (虽然作为第二轮验证它不会不受欢迎)当我在dao中调用hibernate save或update方法时,我怎么能告诉hibernate验证器处理验证,而不是在事务提交时?
以下是我构建的东西的版本:
compile 'org.hibernate:hibernate-entitymanager:4.2.2.Final'
compile 'org.hibernate:hibernate-validator:5.0.1.Final'
compile 'org.hibernate:hibernate-c3p0:4.2.2.Final'
compile 'org.springframework:spring-orm:3.2.3.RELEASE'
compile 'org.springframework:spring-context:3.2.3.RELEASE'
compile 'org.springframework:spring-web:3.2.3.RELEASE'
compile 'javax.inject:javax.inject:1'
compile 'org.aspectj:aspectjrt:1.7.3'
ajc "org.aspectj:aspectjtools:1.7.3"
编辑:还有一个注意事项......我在JPA下做了所有这些,所以如果存在非hibernate特定的解决方案,我会更喜欢。
答案 0 :(得分:0)
当我在dao中调用hibernate save或update方法时,我怎么能告诉hibernate验证器处理验证,而不是在事务提交时?
实现hibernate entity listeners并在您想要进行验证时为每个event type注册事件监听器。
关于如何使用hibernate 4配置事件监听器,请查看this question。
答案 1 :(得分:0)
(Stack Overflow表示这篇文章太长了,无法发表评论,因此请将其作为答案发布。)
我想了一下你正在尝试做什么,并且无法弄清楚动机是什么。所以现在我很好奇。
如果您从其他一些代码接收该对象并希望它是@Valid
,那么您应该在对它进行任何操作之前对其进行验证;不是在致电save()
或update()
时。您可以直接将其传递给验证器,也可以使用Hibernate Validator 5中的新方法约束支持,它将使用代理进行方法验证。
如果你自己构建对象并且在持久化时试图捕获代码错误,那就是代码味道。为什么不首先有构造函数或构造函数来阻止构造无效实体?如果你确实拥有它们,但只是想在持续时进行双重检查,那么如果失败发生在save()
时或交易提交时会有什么不同,因为整个事情应该回滚?
您还应该记住,Hibernate会执行事务性后写,并且会延迟保存,希望它们可以合并在一起以减少与数据库的聊天。因此,如果您希望验证作为保存的副作用,您还应该期望延迟验证或者您正在弄乱语义。
无论如何,我会在DAO级别尝试HV5 method constraints并跳过自定义验证器作为第一种方法。
答案 2 :(得分:0)
我不是100%肯定我喜欢这个,但这就是我解决它的方式。基本上我不再试图找到一个休眠解决方案并添加了一些额外的方法......
在我的服务层:
@Override
public void save(T r) {
// This allows us to post a join point just outside the transaction boundary
save0(r);
}
@Transactional
private void save0(T r) {
getDao().save(r);
}
@Override
public T update(T obj) throws IllegalArgumentException {
// This allows us to post a join point just outside the transaction boundary
return update0(obj);
}
@Transactional
private T update0(T obj) {
return getDao().update(obj);
}
在我的aspectJ切入点
@Around("daoMethods()")
public Object collectDaoMessages(ProceedingJoinPoint thisJoinPoint) throws Throwable {
try {
return thisJoinPoint.proceed();
} catch (Exception e) {
ConstraintViolationException cve;
Throwable cause = e;
while (cause != null && !(cause instanceof ConstraintViolationException)) {
cause = cause.getCause();
}
if (cause == null) {
throw e;
} else {
cve = (ConstraintViolationException) cause;
}
Set<ConstraintViolation<?>> constraintViolations = cve.getConstraintViolations();
List<UserMessage> userMessages = ThreadContext.MESSAGES.get();
for (ConstraintViolation<?> constraintViolation : constraintViolations) {
userMessages.add(new UserMessage(constraintViolation.getMessage(), MessageType.VALIDATION));
}
throw new MyPersistenceException("Validation failures", e);
}
}
@Pointcut(
"execution(public * *(..)) " +
"&& !execution(public * load*(..)) " +
"&& within(com.myclient.service.impl..*) "
)
public void daoMethods() {}