@Autowired bean在控制器上与@Valid一起使用但在CRUD存储库中失败

我正在使用用户注册表单处理Spring MVC + Hibernate + JPA应用程序,我决定使用JSR-303validation程序检查用户名是否已存在于DB中:

public class UniqueUsernameValidator implements ConstraintValidator { @Autowired UserService userService; @Override public void initialize(VerifyUniqueUsername constraintAnnotation) { } @Override public boolean isValid(String username, ConstraintValidatorContext context) { return username!=null && userService.findByUsername(username) == null; } } 

它非常简单,validation在我的控制器上运行良好:

 .... public String signup(@Valid @ModelAttribute("newUser") User user, BindingResult newUserBeanResult) ..... 

我面临的当前问题是,在我validation了User对象之后,我调用了:

 userService.save(user); 

哪个实现了CrudRepository ,我得到一个NullPointerException 。 出于某种原因, UserService在控制器上的validation期间注入,但在调用CrudRepository.save()时则不会

我在Sessionfactory.getCurrentSession.merge调用时看到了类似的post: @Autowired bean null在ConstraintValidator中调用了这个: hibernatevalidation器没有使用autowire,但我想知道是否有人遇到过这个问题。 我认为注入bean来访问validation器上的数据库是相当普遍的。

作为一种解决方法,我在userService上添加了一个null检查,但感觉不对。

  1. 这是预期的行为吗? 在调用CrudRepository.save()之前,这些validation是否会被CrudRepository.save()
  2. 我是否愿意处理“手动”hibernate事件? 在这种情况下pre-insert

我最终通过指示Spring的EntityManagerFactoryBean使用我的validation器bean来解决这个问题(更准确地说,hibernate现在将使用Spring的validation器):

              org.hibernate.dialect.MySQLDialect 3 50 10 true    

但是,这引发了StackOverflow错误:)

显然,这个问题的原因是我的validation器使用了finder方法( findByUsername ),finder方法触发了一个hibernate flush,后者又触发了validation。 这无限循环,直到你得到最着名的例外。

所以…我通过更改validation器直接使用EntityManager(而不是CRUD存储库)并暂时将FlushModeType更改为COMMIT来解决此问题。 这是一个例子:

 public class UniqueUsernameValidator implements ConstraintValidator { @PersistenceContext private EntityManager em; @Autowired UserService userService; @Override public void initialize(UniqueUsername constraintAnnotation) { } @Override public boolean isValid(String username, ConstraintValidatorContext context) { try { em.setFlushMode(FlushModeType.COMMIT); return userService.findByUsername(username) == null; } finally { em.setFlushMode(FlushModeType.AUTO); } } } 

这解决了validation器使用finder函数触发hibernate flush的问题,而hibernate flush又触发了validation器导致StackOverflowError。

当响应save方法调用validation逻辑时,它由hibernate完成。 validation器对象由hibernate创建,因此spring @AutoWired将无法工作。

修复此问题的一个选项是使用@Configurable注释并启用加载时间编织,以确保即使在hibernate实例化validation器对象时,spring也会将依赖项注入其中。