Main question
In my web app, implemented with Spring/Hibernate/Thymeleaf, I have two domain objects, Owner and Pet. The Pet object has an (optional) Owner field. During an update of a pet through a web form, the user may enter an owner id that exists (in the database) or not.
When the user updates a pet with an existing owner id, the existing owner object is found and associated with the pet. However, when the user updates a pet with an owner id that is not present in the database, the returned pet object (in the "POST" controller method) has a null owner object. In addition, as the owner field within the pet domain could be null, this raises no validation errors.
Ultimately, this may lead to a user submitting erroneous values, thinking that they have added an owner, only for the update of a pet to actually take place with a null owner (if not properly caught).
My question is, what is the recommended way of validating that the id of a nested object exists before update of the parent object is executed.
Further thoughts:
- I have seen a relevant issue: spring mvc nested model validation, where it is recommended to add a custom validator. In my case, adding a custom validator, that will run within the PetController and validate the Pet/ Owner object will, I think, not work. The returned pet object contains a null owner object, which is a perfectly valid state. I need the validation to happen during data binding. Once we are inside the controller, post-binding, the owner object is null and no validation of the form values can happen.
- I have done some work on it by adding a custom formatter, from String to Owner. If the owner id cannot be found, this formatter will return a new Owner object with id of "Not Found" (or some numeric/integer equivalent in this case). In my controller, I could then check for an owner with the "erroneous id" and raise an error. However, the default behavior of not returning an object seems safer, as my custom formatter actually returns an invalid object (that may lead to other issues down the line?).
Code
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Pet {
@Id
private int id;
private String name;
@ManyToOne
@Valid
private Owner owner;
}
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Owner {
@Id
@NotNull
@NonNull
private int id;
private String firstName;
private String lastName;
}
@Controller
public class PetController {
@Autowired
PetRepository petRepository;
@RequestMapping("editPet")
@GetMapping
public String editPet(Model model) {
Pet pet = petRepository.getById(1);
model.addAttribute("pet", pet);
return "editPet";
}
@RequestMapping("updatePet")
@PostMapping
public String updatePet(@Validated Pet pet, BindingResult bindingResult) {
petRepository.save(pet); // if the user submits an invalid owner id, the returned pet will have a null owner
// object, but no error raised.
return "editPet";
}
}
}