Spring 引导 - Hibernate 自定义约束不注入服务
Spring Boot - Hibernate custom constraint doesn't inject Service
我会尽量忽略其他细节并使其简短:
@Entity
public class User
@UniqueEmail
@Column(unique = true)
private String email;
}
@Component
public class UniqueEmailValidatior implements ConstraintValidator<UniqueEmail,String>, InitializingBean {
@Autowired private UserService userService;
@Override
public void initialize(UniqueEmail constraintAnnotation) {
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if(userService == null) throw new IllegalStateException();
if(value == null) return false;
return !userService.isEmailExisted(value);
}
}
这将在 Spring 中进行验证时起作用(Spring MVC @Valid
或使用 @Autowire
注入 Validator
),一切都会美好的。
但是一旦我使用 Spring Data JPA 保存实体:
User save = userRepository.save(newUser);
Hibernate 将尝试在不注入 UserService
bean 的情况下实例化一个新的 UniqueEmailValidatior
。
那么,如何让 Hibernate 在不实例化新组件的情况下使用我的 UniqueEmailValidatior
组件。
我可以使用 spring.jpa.properties.javax.persistence.validation.mode=none
禁用休眠验证,但我希望有另一种方法
更新:这是我的用户服务:
@Autowired private Validator validator;
@Transactional
public SimpleUserDTO newUser(UserRegisterDTO user) {
validator.validate(user);
System.out.println("This passes");
User newUser = new User(user.getUsername(),
passwordEncoder.encode(user.getPassword()),user.getEmail(),
"USER",
user.getAvatar());
User save = userRepository.save(newUser);
System.out.println("This won't pass");
return ....
}
这里post的答案很大。请查看 S.O 中的这篇文章以帮助您。这应该可以帮助您入门。
Test Custom Validator with Autowired spring Service
问题是 hibernate 无法知道 spring 定义。但是,您可以让实体了解任何类型的 javax.validation
类型。希望这有帮助。
要将 Spring bean 注入您的 ConstraintValidator
,您需要一个特定的 ConstraintValidatorFactory
,它应该在 ValidatorFactory
.[=19 的初始化时传递=]
大致如下:
ValidatorFactory validatorFactory = Validation.byDefaultProvider()
.configure()
.constraintValidatorFactory( new MySpringAwareConstraintValidatorFactory( mySpringContext ) )
.build();
其中 MySpringAwareConstraintValidatorFactory
是一个 ConstraintValidatorFactory
将 bean 注入您的 ConstraintValidator
.
我怀疑 Spring 数据使用的 ValidatorFactory
在创建它们时没有注入验证器,这很不幸。
我想您应该可以覆盖它。或者更好的是,您应该针对 Spring Boot/Spring 数据提出一个问题,以便他们正确地注入 ConstraintValidator
s 因为它是我们连续第二次在 SO 上提出这个问题。
我希望 Spring Boot 会将现有的验证器连接到 EntityManager
显然它不会。
您可以使用 HibernatePropertiesCustomizer
并向现有 EntityManagerFactoryBuilder
添加属性并注册 Validator
。
注意: 我在这里假设您使用的是 Spring Boot 2.0
@Component
public class ValidatorAddingCustomizer implements HibernatePropertiesCustomizer {
private final ObjectProvider<javax.validation.Validator> provider;
public ValidatorAddingCustomizer(ObjectProvider<javax.validation.Validator> provider) {
this.provider=provider;
}
public void customize(Map<String, Object> hibernateProperties) {
Validator validator = provider.getIfUnique();
if (validator != null) {
hibernateProperties.put("javax.persistence.validation.factory", validator);
}
}
}
像这样的东西应该将现有的验证器与休眠连接起来,并且它将使用自动连接。
注意: 您不需要在验证器上使用 @Component
自动装配在返回 [=14= 的实例之前构建到验证器工厂中].
我会尽量忽略其他细节并使其简短:
@Entity
public class User
@UniqueEmail
@Column(unique = true)
private String email;
}
@Component
public class UniqueEmailValidatior implements ConstraintValidator<UniqueEmail,String>, InitializingBean {
@Autowired private UserService userService;
@Override
public void initialize(UniqueEmail constraintAnnotation) {
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if(userService == null) throw new IllegalStateException();
if(value == null) return false;
return !userService.isEmailExisted(value);
}
}
这将在 Spring 中进行验证时起作用(Spring MVC @Valid
或使用 @Autowire
注入 Validator
),一切都会美好的。
但是一旦我使用 Spring Data JPA 保存实体:
User save = userRepository.save(newUser);
Hibernate 将尝试在不注入 UserService
bean 的情况下实例化一个新的 UniqueEmailValidatior
。
那么,如何让 Hibernate 在不实例化新组件的情况下使用我的 UniqueEmailValidatior
组件。
我可以使用 spring.jpa.properties.javax.persistence.validation.mode=none
禁用休眠验证,但我希望有另一种方法
更新:这是我的用户服务:
@Autowired private Validator validator;
@Transactional
public SimpleUserDTO newUser(UserRegisterDTO user) {
validator.validate(user);
System.out.println("This passes");
User newUser = new User(user.getUsername(),
passwordEncoder.encode(user.getPassword()),user.getEmail(),
"USER",
user.getAvatar());
User save = userRepository.save(newUser);
System.out.println("This won't pass");
return ....
}
这里post的答案很大。请查看 S.O 中的这篇文章以帮助您。这应该可以帮助您入门。
Test Custom Validator with Autowired spring Service
问题是 hibernate 无法知道 spring 定义。但是,您可以让实体了解任何类型的 javax.validation
类型。希望这有帮助。
要将 Spring bean 注入您的 ConstraintValidator
,您需要一个特定的 ConstraintValidatorFactory
,它应该在 ValidatorFactory
.[=19 的初始化时传递=]
大致如下:
ValidatorFactory validatorFactory = Validation.byDefaultProvider()
.configure()
.constraintValidatorFactory( new MySpringAwareConstraintValidatorFactory( mySpringContext ) )
.build();
其中 MySpringAwareConstraintValidatorFactory
是一个 ConstraintValidatorFactory
将 bean 注入您的 ConstraintValidator
.
我怀疑 Spring 数据使用的 ValidatorFactory
在创建它们时没有注入验证器,这很不幸。
我想您应该可以覆盖它。或者更好的是,您应该针对 Spring Boot/Spring 数据提出一个问题,以便他们正确地注入 ConstraintValidator
s 因为它是我们连续第二次在 SO 上提出这个问题。
我希望 Spring Boot 会将现有的验证器连接到 EntityManager
显然它不会。
您可以使用 HibernatePropertiesCustomizer
并向现有 EntityManagerFactoryBuilder
添加属性并注册 Validator
。
注意: 我在这里假设您使用的是 Spring Boot 2.0
@Component
public class ValidatorAddingCustomizer implements HibernatePropertiesCustomizer {
private final ObjectProvider<javax.validation.Validator> provider;
public ValidatorAddingCustomizer(ObjectProvider<javax.validation.Validator> provider) {
this.provider=provider;
}
public void customize(Map<String, Object> hibernateProperties) {
Validator validator = provider.getIfUnique();
if (validator != null) {
hibernateProperties.put("javax.persistence.validation.factory", validator);
}
}
}
像这样的东西应该将现有的验证器与休眠连接起来,并且它将使用自动连接。
注意: 您不需要在验证器上使用 @Component
自动装配在返回 [=14= 的实例之前构建到验证器工厂中].