I have a controller and i'm validating ModelAttribute through a validator before updating the entity like the code below. The validator receives the object to be updated in the obj variable.
The problem i have is that the modified object (obj) in validate method is saved to database when i run this method restaurantRepository.findByEmail. It has something to do with @Transactional. I don't understand why it saves the obj, I haven't passed it onto the method.
I have read this SO post Why does @Transactional save automatically to database The answer says that
Before the transactional method is about to return, the transaction commits, meaning all changes to the managed entities are flushed to the database.
But i understand that the managed entities are the entities existing inside the scope of @Transactional. I haven't passed obj into the method so i don't understand why it's saving automatically.
Restaurant
@Entity
@Table(name="restaurant")
@SequenceGenerator(name="restaurant_seq", sequenceName="restaurant_seq")
public class Restaurant{
private String name;
private String email;
private String phonenumber;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhonenumber() {
return phonenumber;
}
public void setPhonenumber(String phonenumber) {
this.phonenumber = phonenumber;
}
}
RestaurantContoller
@RequestMapping("/restaurant")
@Controller
public class RestaurantController {
@Autowired
private RestaurantService restaurantRepository;
@Autowired
private RestaurantValidator restaurantValidator;
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(restaurantValidator);
}
@GetMapping("/{id}")
public String viewRestaurant(@PathVariable("id") Long restaurant_id, final ModelMap model) {
Restaurant restaurant = restaurantService.findById(restaurant_id);
if(!model.containsAttribute("restaurantModel")){
model.addAttribute("restaurantModel", restaurant );
}
return "pages/restaurant_view_new";
}
@PatchMapping("/{restaurantModel}")
public String updateRestaurant(@Valid @ModelAttribute("restaurantModel") Restaurant editRestaurant, BindingResult bindingResult, final ModelMap model, RedirectAttributes redirectAttributes) {
if (bindingResult.hasErrors()) {
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.restaurantModel", bindingResult);
redirectAttributes.addFlashAttribute("restaurantModel", editRestaurant);
redirectAttributes.addFlashAttribute("saveErrors", true);
return "redirect:/restaurant/" + editRestaurant.getId();
}
restaurantRepository.save(editRestaurant);
redirectAttributes.addFlashAttribute("saveSuccess", true);
return "redirect:/restaurant/" + editRestaurant.getId();
}
}
RestaurantValidator
@Component
public class RestaurantValidator implements Validator {
@Autowired
RestaurantRepository restaurantRepository;
public boolean supports(Class clazz) {
return Restaurant.class.equals(clazz);
}
public void validate(Object obj, Errors e) {
ValidationUtils.rejectIfEmpty(e, "name", "form.required");
ValidationUtils.rejectIfEmpty(e, "email", "form.required");
ValidationUtils.rejectIfEmpty(e, "phonenumber", "form.required");
Restaurant p = (Restaurant) obj;
if(restaurantRepository.findByEmail(p.getEmail()).size() > 0 ){
e.rejectValue("email", "form.email.duplicate");
}
}
}
RestaurantRepository
@Transactional
public interface RestaurantRepository extends JpaRepository<Restaurant, Long>{
List<Restaurant> findByEmail(String email);
}