问题描述
我正在使用带有用户注册表的Spring MVC + Hibernate + JPA应用程序工作,我决定使用JSR-303验证程序来检查数据库中是否已存在用户名:
I'm working on a Spring MVC + Hibernate + JPA app with a user registration form and I decided to use a JSR-303 validator to check whether the username already existed in the DB:
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;
}
}
这非常简单,验证在我的控制器上效果很好:
It is very straightforward and validation worked great on my controller:
....
public String signup(@Valid @ModelAttribute("newUser") User user, BindingResult newUserBeanResult)
.....
我目前面临的问题是,在我验证了User
对象并致电后:
The current problem I'm facing is that after I get my User
object validated and I call:
userService.save(user);
实现了CrudRepository
的我得到了NullPointerException
.由于某些原因,UserService
是在控制器验证期间注入的,但在我调用CrudRepository.save()
时是不是的.
Which implements CrudRepository
, I get a NullPointerException
. For some reason UserService
is injected during validation on the controller but not when I call CrudRepository.save()
.
我看到了类似的帖子,例如: @ConstraintValidator中的Autowired bean null,由Sessionfactory.getCurrentSession调用时.merge 还有这个:休眠验证器,无需使用自动装配但我想知道以前是否有人遇到过这种情况.我认为注入bean来访问验证器上的数据库是相当普遍的.
I saw similar posts such as this:@Autowired bean null in ConstraintValidator when invoked by Sessionfactory.getCurrentSession.mergeand this:hibernate validator without using autowirebut I was wondering if someone has run into this before. I would think that injecting beans to access the database on a validator is fairly common.
作为一种解决方法,我在userService
上添加了一个null检查,但感觉不正确.
As a workaround I added a check for null on userService
but it doesn't feel right.
- 这是预期的行为吗?是否可以在调用
CrudRepository.save()
之前触发这些验证? - 我应该处理手动"休眠事件吗?在这种情况下
pre-insert
- Is this expected behaviour? Are these validations supossed to fire before calling
CrudRepository.save()
? - Am I suppossed to handle "manually" hibernate events? In this case
pre-insert
推荐答案
我最终通过指示Spring的EntityManagerFactoryBean
使用我的验证器bean(更准确地说,休眠将使用Spring的验证器)解决了这个问题:
I ended up solving this issue by instructing Spring's EntityManagerFactoryBean
to use my validator bean (more accurately, hibernate will now be using Spring's validator):
<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>
但是,这引发了StackOverflow错误:)
However, this threw a StackOverflow error :)
显然,此问题的原因是我的验证器正在使用finder方法(findByUsername
),并且finder方法触发了休眠刷新,这又触发了验证.这将无限循环,直到您得到最著名的异常为止.
Apparently the cause of this issue was that my validator was using a finder method (findByUsername
) and finder methods trigger a hibernate flush, which in turn triggers a validation. This loops indefinitely until you get the most famous exception there is.
所以...我通过更改验证器以直接使用EntityManager(而不是CRUD存储库)并将FlushModeType临时更改为COMMIT来解决此问题.这是示例:
So...I fixed this by changing the validators to use the EntityManager directly (instead of the CRUD repository) and temporarily change the FlushModeType to COMMIT. Here is the example:
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);
}
}
}
这解决了验证器使用查找程序功能触发休眠刷新的问题,而休眠刷新又触发了验证器,从而导致StackOverflowError.
This solves the issue where the validator was using the finder function which triggered a hibernate flush which in turn was triggering the validator causing a StackOverflowError.
这篇关于@Autowired bean在控制器上与@Valid一起使用,但对于CRUD存储库失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!