I have a spring project and want to enforce uniqueness in the database on a field and get the error message back to the UI.
I have read this SO answer and it makes sense so @Column(unique = true) makes the constraint on the table but doesn't enforce it.
So the question becomes how to create a @Unique annotation that checks with the database and returns a error message into BindingResult on POST handler.
An example would be great.
UPDATE
I tried the following way to make a custom validator:
The objects (note I have added @valid to get the validator messages to navigate up to BindingResult)
Person.java
@Entity
public class Person {
public Person() {}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// other stuff
@UniqueNid
private BigInteger nid;
EpisodePerson.java
@Entity
public class EpisodePerson {
public EpisodePerson(){};
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@Valid
private Person person;
EpisodeViewModel (DTO)
public class EpisodeViewModel {
@Valid
private Episode episode = new Episode();
@Valid
private List<EpisodePerson> persons = new ArrayList<>();
UniqueNid.java
@Documented
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueNiaValidator.class)
public @interface UniqueNid {
String message() default "{Duplicate ID}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
UniqueNidValidator.java
public class UniqueNidValidator implements ConstraintValidator<UniqueNid, BigInteger> {
public UniqueNidValidator(){};
private PersonRepository personRepository;
@Autowired
public void setPersonRepository(PersonRepository personRepository) {this.personRepository = personRepository;}
public UniqueNidValidator(PersonRepository personRepository) {
this.personRepository = personRepository;
}
@Override
public void initialize(UniqueNid constraint) {
}
@Override
public boolean isValid(BigInteger nid, ConstraintValidatorContext context) {
return nid != null && personRepository.existsByNid(nid);
}
}
PersonRepository.java
...
Boolean existsByNid(BigInteger nid);
...
Application.java
@SpringBootApplication
@EnableAutoConfiguration(exclude = { org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration.class })
public class Demo3Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(WebApplicationInitializer.class);
}
public static void main(String[] args) {
SpringApplication.run(Demo3Application.class, args);
}
@Bean
public javax.validation.Validator localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
}
When I go to submit a person I get :
Stack Trace (abbreviated)
java.lang.NullPointerException: null
at com.example.validators.UniqueNidValidator.isValid(UniqueNidValidator.java:31) ~[main/:na]
UPDATE 2
I have also tried this configuration
public class UniqueNidValidator implements ConstraintValidator<UniqueNid, BigInteger> {
public UniqueNidValidator(){};
private PersonRepository personRepository;
public UniqueNidValidator(PersonRepository personRepository) {
this.personRepository = personRepository;
}
@Override
public void initialize(UniqueNid constraint) {
}
@Override
public boolean isValid(BigInteger nid, ConstraintValidatorContext context) {
System.out.println("About to check " +nid.toString());
System.out.println("person repo " +personRepository.toString() );
return personRepository.existsByNid(nid);
}
}
which gives:
java.lang.NullPointerException: null
at com.example.validators.UniqueNiaValidator.isValid(UniqueNiaValidator.java:29) ~[main/:na]
When I try to print the repo to console.