弹簧数据JPA合并更新实体

问题描述:

我已经尝试了一段时间来更新使用Spring Boot + Spring Data JPA的实体。我收到了所有正确的观点。我的编辑视图通过ID向我返回正确的实体。一切都很好..直到我真的试图保存/合并/坚持对象。 单次我回来一个新的实体与一个新的ID。我只是不知道为什么。我在网上查了一些例子,以及你可能会提及的重复问题的链接。那么,我在这些代码中犯了什么错误?弹簧数据JPA合并更新实体

package demo; 

    import javax.persistence.Column; 
    import javax.persistence.Entity; 
    import javax.persistence.GeneratedValue; 
    import javax.persistence.GenerationType; 
    import javax.persistence.Id; 
    import javax.persistence.Table; 

    @Entity 
    @Table(name = "ORDERS") 
    public class Order { 

     @Id 
     @GeneratedValue(strategy = GenerationType.AUTO) 
     private Integer id; 

     @Column(name = "ORDER_NAME") 
     private String name; 

     @Column(name = "ORDER_DESCRIPTION") 
     private String description; 

     @Column(name = "ORDER_CONTENT") 
     private String content; 

     public Order() {} 

     public Order(String name, String description, String content) { 
      this.name = name; 
      this.description = description; 
      this.content = content; 
     } 

     public String getContent() { 
      return content; 
     } 

     public String getDescription() { 
      return description; 
     } 

     public String getName() { 
      return name; 
     } 

     public Integer getId() { 
      return this.id; 
     } 

     public void setContent(String content) { 
      this.content = content; 
     } 

     public void setDescription(String description) { 
      this.description = description; 
     } 

     public void setName(String name) { 
      this.name = name; 
     } 

     @Override 
     public boolean equals(Object obj) { 
      if (this == obj) 
       return true; 
      if (obj == null) 
       return false; 
      if (getClass() != obj.getClass()) 
       return false; 
      Order other = (Order) obj; 
      if (content == null) { 
       if (other.content != null) 
        return false; 
      } else if (!content.equals(other.content)) 
       return false; 
      if (description == null) { 
       if (other.description != null) 
        return false; 
      } else if (!description.equals(other.description)) 
       return false; 
      if (id == null) { 
       if (other.id != null) 
        return false; 
      } else if (!id.equals(other.id)) 
       return false; 
      if (name == null) { 
       if (other.name != null) 
        return false; 
      } else if (!name.equals(other.name)) 
       return false; 
      return true; 
     } 

     @Override 
     public int hashCode() { 
      final int prime = 31; 
      int result = 1; 
      result = prime * result + ((content == null) ? 0 : content.hashCode()); 
      result = prime * result 
        + ((description == null) ? 0 : description.hashCode()); 
      result = prime * result + ((id == null) ? 0 : id.hashCode()); 
      result = prime * result + ((name == null) ? 0 : name.hashCode()); 
      return result; 
     } 

     @Override 
     public String toString() { 
      return "Order [id=" + id + ", name=" + name + ", description=" 
        + description + ", content=" + content + "]"; 
     } 

    } 





    package demo; 

    import org.springframework.data.jpa.repository.JpaRepository; 

    public interface OrderRepository extends JpaRepository<Order, Integer> { 

     public Order findByName(String name); 


    } 

package demo;

import javax.persistence.EntityManager; 
    import javax.persistence.PersistenceContext; 
    import javax.transaction.Transactional; 

    import org.springframework.stereotype.Service; 

    @Service("customJpaService") 
    public class CustomJpaServiceImpl implements CustomJpaService{ 

     @PersistenceContext 
     private EntityManager em; 

     @Transactional 
     public Order saveOrUpdateOrder(Order order) { 

      if (order.getId() == null) { 
       em.persist(order); 
      } else { 
       em.merge(order); 
      } 
      return order; 
     } 

    } 

package demo;

import java.util.List; 

    import org.springframework.beans.factory.annotation.Autowired; 
    import org.springframework.stereotype.Controller; 
    import org.springframework.validation.BindingResult; 
    import org.springframework.web.bind.annotation.ModelAttribute; 
    import org.springframework.web.bind.annotation.PathVariable; 
    import org.springframework.web.bind.annotation.RequestMapping; 
    import org.springframework.web.bind.annotation.RequestMethod; 
    import org.springframework.web.servlet.ModelAndView; 
    import org.springframework.web.servlet.mvc.support.RedirectAttributes; 

    @Controller 
    public class OrderController { 

     //refactor to service with 
     //logging features 
     @Autowired 
     OrderRepository orderRepo; 

     @Autowired 
     CustomJpaService customJpaService; 

     @RequestMapping(value="/orders", method=RequestMethod.GET) 
     public ModelAndView listOrders() { 

      List<Order> orders = orderRepo.findAll(); 

      return new ModelAndView("orders", "orders", orders); 

     } 

     @RequestMapping(value="/orders/{id}", method=RequestMethod.GET) 
     public ModelAndView showOrder(@PathVariable Integer id, Order order) { 
      order = orderRepo.findOne(id); 
      return new ModelAndView("showOrder", "order", order); 
     } 

     @RequestMapping(value="/orders/edit/{id}", method=RequestMethod.GET) 
     public ModelAndView editForm(@PathVariable("id") Integer id) { 
      Order order = orderRepo.findOne(id); 
      return new ModelAndView("editOrder", "order", order); 
     } 

     @RequestMapping(value="/updateorder", method=RequestMethod.POST) 
     public String updateOrder(@ModelAttribute("order") Order order, BindingResult bindingResult, final RedirectAttributes redirectattributes) { 

      if (bindingResult.hasErrors()) { 
       return "redirect:/orders/edit/" + order.getId(); 
      } 

      customJpaService.saveOrUpdateOrder(order); 
      redirectattributes.addFlashAttribute("successAddNewOrderMessage", "Order updated successfully!"); 
      return "redirect:/orders/" + order.getId(); 
     } 


     @RequestMapping(value="/orders/new", method=RequestMethod.GET) 
     public ModelAndView orderForm() { 
      return new ModelAndView("newOrder", "order", new Order()); 
     } 

     @RequestMapping(value="/orders/new", method=RequestMethod.POST) 
     public String addOrder(Order order, final RedirectAttributes redirectAttributes) { 
      orderRepo.save(order); 
      redirectAttributes.addFlashAttribute("successAddNewOrderMessage", "Success! Order " + order.getName() + " added successfully!"); 
      return "redirect:/orders/" + order.getId(); 
     } 

    } 

此代码后。我的看法返回到正确的URL,但ID为4 < - 这是一个新的实体。它应该说3与更新的属性。

+0

那么我做了一些测试,似乎在更新实体的POST方法中,order对象返回的ID为null。我不知道为什么模型属性出现在表单中,我使用@ModelAttribute注解。 – DtechNet

您需要在GET请求和POST请求之间存储实体。您的选择:

  1. 在文章的开头重新从数据库中的实体,然后从张贴的实体
  2. 存放在隐藏的表单变量的实体信息
  3. 商店的实体在会议
  4. 复制其属性

3是唯一正确的解决方案,因为它允许乐观的并发控制,并且比隐藏的形式变量更安全。

@SessionAttributes("modelAttributeName")添加到控制器的顶部,并将SessionStatus参数添加到POST处理程序方法中。完成后致电sessionStatus.setComplete()。有关工作示例,请参见Spring MVC: Validation, Post-Redirect-Get, Partial Updates, Optimistic Concurrency, Field Security

+0

你只对API使用“method = RequestMethod.PUT”吗?因为我注意到JSP和Spring表单标签不支持“PUT”。所以我猜你仍然使用POST方法进行“更新”。实际更新发生在数据层。另外,你是否必须在实体上使用@Version? – DtechNet

+0

其实它不是网页浏览器,不会从html表单中删除/删除,而不仅仅是弹簧。虽然可以使用put/delete w Ajax。你只需要@version进行乐观锁定 –