Custom Validators in Java
So, you’ve got a signup form, and someone tries to register as kingkong123 for the 14th time. What do you do? Throw the whole server away? Nope. You create a custom validator to ensure usernames are unique.
Get Samuelgbenga’s stories in your inbox
Join Medium for free to get updates from this writer.
Let’s go step by step — short, sweet, and practical.
1. Entity Setup
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@UniqueUsername // <- Our custom annotation
private String username;
private String email;
private String password;
// Getters and setters
}2. Custom Annotation
@Constraint(validatedBy = UniqueUsernameValidator.class)
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface UniqueUsername {
String message() default "Username already exists";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}3. Validator Class
@Component
@RequiredArgsConstructor
public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {
private final UserRepository userRepository;
@Override
public boolean isValid(String username, ConstraintValidatorContext context) {
return username != null && !userRepository.existsByUsername(username);
}
}4. Repository Layer
public interface UserRepository extends JpaRepository<User, Long> {
boolean existsByUsername(String username);
}5. Controller Layer
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping("/register")
public ResponseEntity<String> registerUser(@Valid @RequestBody User user) {
userService.saveUser(user);
return ResponseEntity.ok("User registered successfully!");
}
}6. Service Layer
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
public void saveUser(User user) {
userRepository.save(user);
}
}7. Global Exception Handling (Make it Reusable)
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Object> handleValidationException(MethodArgumentNotValidException ex) {
Map<String, Object> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage()));
return buildErrorResponse(HttpStatus.BAD_REQUEST, "Validation failed", errors);
}
private ResponseEntity<Object> buildErrorResponse(HttpStatus status, String message, Map<String, Object> errors) {
Map<String, Object> response = new HashMap<>();
response.put("status", status.value());
response.put("message", message);
response.put("errors", errors);
return new ResponseEntity<>(response, status);
}
}Final Words
There you go. You now have a working custom validator in Java. Say goodbye to duplicate usernames and hello to clean data.









