My question is related to bean validation. Apparently SpringBoot comes with two different validation mechanisms, one JSR-303
compilant (in javax.validation
package) and the other provided by Spring framework (in org.springframework.validation
package).
While it is easy to enforce JSR-303
validation via @Validated
and @Valid
annotations, I couldn't find the proper way for Spring's ones.
Take the following User
bean.
@Entity
public class User {
@Column
@Id
private String id;
@Column(unique = true)
@Username
private String username;
// [...]
}
Where @Username
constraint is defined as follows. Basically, it's just a composition of @Pattern
and @Size
costraints.
@Constraint(validatedBy = {})
@Documented
@Pattern(regexp = "[A-Za-z0-9_]+")
@Retention(RUNTIME)
@Size(max = 24, min = 3)
@Target(FIELD)
public @interface Username {
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
User
beans are stored in a repository named UserRepository
, defined as follows.
@Repository
public interface UserRepository extends CrudRepository<User, String>, JpaSpecificationExecutor<User> {
}
To access the repository I wrote a service, shown below.
@Transactional
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Validated
public void create(@Valid User user) {
userRepository.save(user);
}
}
Until now, things are pretty neat and everything works. With just a couple of annotations I've achieved everything.
Now, I have this other validator (not JSR-303
).
// [...]
import org.springframework.validation.Validator;
@Component
public class UniqueUsernameValidator implements Validator {
@Autowired
private UserRepository userRepository;
@Override
public boolean supports(Class<?> clazz) {
return User.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
User user = (User) target;
if (userRepository.count(where(usernameIsEqualTo(user.getUsername()))) > 0) {
errors.rejectValue("username", "username.exists");
}
}
}
Ideally, I would like to have it enforced via the same @Validated
and @Valid
annotations but until now I've been unlucky. I guess it is definitely possible given that the documentation of the class org.springframework.validation.Validator
says
This interface is totally divorced from any infrastructure or context; that is to say it is not coupled to validating only objects in the web tier, the data-access tier, or the whatever-tier. As such it is amenable to being used in any layer of an application , and supports the encapsulation of validation logic as a first-class citizen in its own right.
@Valid
is a JSR-303 bean validation annotation, and it won't call your UniqueUsernameValidator
Spring Validator.
If you want to do that in your service, you need to invoke it manually:
public void create(@Valid User user) {
Errors errors = new BeanPropertyBindingResult(user, "user");
// Validator component has been previously injected into the service.
uniqueUsernameValidator.validate(user, errors);
if (errors.hasErrors()) {
throw new RuntimeException(errors.toString());
}
userRepository.save(user);
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.