为什么@Transactional将实体范围外的实体保存到db?

时间:2018-03-07 07:30:56

标签: java spring hibernate jpa transactional

我有一个控制器,我在更新实体之前通过验证器验证ModelAttribute,如下面的代码所示。验证器在obj变量中接收要更新的对象。

我遇到的问题是当我运行这个方法restaurantRepository.findByEmail时,validate方法中的修改对象(obj)被保存到数据库中。它与@Transactional有关。我不明白为什么它会保存obj,我还没有将它传递给方法。

我已阅读此SO帖子 Why does @Transactional save automatically to database 答案是

  

在事务方法即将返回之前,事务提交,这意味着对托管实体的所有更改都将刷新到数据库。

但据我所知,托管实体是@Transactional范围内的实体。我没有通过obj进入方法,所以我不明白为什么它会自动保存。

餐厅

@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);
}

2 个答案:

答案 0 :(得分:0)

根据我的经验,当您调用一个SELECT语句,该语句涉及在持久化上下文中查询已修改实体的数据库表时,会触发刷新。

我认为这样做的原因是Hibernate说&#34;你正在桌子上做一个选择,可能包括我在这里的未刷新实体,然后我可能会对两者中哪一个最有冲突电流&#34;

我不知道这是否与特定的FLUSH策略有关。

答案 1 :(得分:0)

问题是缺少一些细节,但我的假设是obj处于持久状态。
查看更多详情here

因此,在提交事务时,任何修改都会自动与数据库同步。

如果您不想进行同步修改,则需要在修改对象之前将其分离 见

EntityManager.detach(Object var)
EntityManager.persits(Object var)

在您上次发表评论后,我建议您阅读以下内容Why is Hibernate Open Session in View considered a bad practice?