@Autowired bean在控制器上与@Valid一起使用,但对于CRUD存储库失败
问题内容:
我正在使用带有用户注册表的Spring MVC + Hibernate +
JPA应用程序工作,我决定使用JSR-303验证程序来检查数据库中是否已存在用户名:
public class UniqueUsernameValidator implements ConstraintValidator<VerifyUniqueUsername, String> {
@Autowired
UserService userService;
@Override
public void initialize(VerifyUniqueUsername constraintAnnotation) {
}
@Override
public boolean isValid(String username, ConstraintValidatorContext context) {
return username!=null && userService.findByUsername(username) == null;
}
}
这非常简单,验证在我的控制器上效果很好:
....
public String signup(@Valid @ModelAttribute("newUser") User user, BindingResult newUserBeanResult)
.....
我目前面临的问题是,在User
验证对象并致电后:
userService.save(user);
哪个工具CrudRepository
,我得到一个NullPointerException
。由于某种原因UserService
,在验证期间在控制器上注入了消息,但在我致电时却没有 注入CrudRepository.save()
。
但是我想知道以前是否有人遇到过这种情况。我认为注入bean来访问验证器上的数据库是相当普遍的。
作为一种解决方法,我添加了对null的检查,userService
但感觉不对。
- 这是预期的行为吗?是否可以在致电之前触发这些验证
CrudRepository.save()
? - 我是否应该处理“手动”hibernate事件?在这种情况下
pre-insert
问题答案:
我最终通过指示SpringEntityManagerFactoryBean
使用我的验证器bean(更准确地说,hibernate将使用Spring的验证器)解决了这个问题:
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="packagesToScan" value="some.packages"/>
<property name="jpaPropertyMap">
<map>
<entry key="javax.persistence.validation.factory" value-ref="validator" />
</map>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.max_fetch_depth">3</prop>
<prop key="hibernate.jdbc.fetch_size">50</prop>
<prop key="hibernate.jdbc.batch_size">10</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
但是,这引发了错误:)
显然,此问题的原因是我的验证器正在使用finder方法(findByUsername
),并且finder方法触发了hibernate刷新,这又触发了验证。这将无限循环,直到您获得最著名的异常为止。
所以…我通过更改验证器以直接使用EntityManager(而不是CRUD存储库)并暂时将FlushModeType更改为COMMIT来解决此问题。这是示例:
public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {
@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);
}
}
}
这解决了验证器使用查找程序功能触发hibernate刷新的问题,而hibernate刷新又触发了验证器,从而导致StackOverflowError。