@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检查,但感觉不对。
- 这是预期的行为吗? 在调用
CrudRepository.save()
之前,这些validation是否会被CrudRepository.save()
? - 我是否愿意处理“手动”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也会将依赖项注入其中。