2013-10-01 22 views
1

我一直在摔跤如何實現一個窗體,在我使用Spring 3和Hibernate 4構建的Web應用程序中創建多對多關係。我是試圖用標記系統構建一個簡單的博客工具。我創建了一個型號爲BlogPost,與型號Tags具有多對多關係。當我創建一個新的BlogPost對象時,標記的Web表單輸入是單行文本輸入。我希望能夠通過空白分割這個文本字符串,並使用它來創建Tag對象。或者,在編輯現有的BlogPost時,我希望能夠將SetTag對象與BlogPost關聯,並將其轉換爲用作輸入元素值的String。我的問題是在使用我的表單在文本輸入和參考的Tag對象之間進行轉換。Spring + Hibernate:多對多的關係和web表單

綁定/獲取/更新與Web表單的多對多關係的最佳做法是什麼?有沒有簡單的方法來做到這一點,我不知道?

UPDATE

我決定,如在下面的回答表明,手動處理的形式和String標籤值的對象模型所需的Set<Tag>對象之間的對象的轉換。這裏是最後的工作代碼:

editBlogPost.jsp

... 
<div class="form-group"> 
    <label class="control-label col-lg-2" for="tagInput">Tags</label> 
    <div class="col-lg-7"> 
     <input id="tagInput" name="tagString" type="text" class="form-control" maxlength="100" value="${tagString}" />     
    </div> 
    <form:errors path="tags" cssClass="help-inline spring-form-error" element="span" /> 
</div> 
.... 

BlogController.java

@Controller 
@SessionAttributes("blogPost") 
public class BlogController { 

    @Autowired 
    private BlogService blogService; 

    @Autowired 
    private TagService tagService; 

    @ModelAttribute("blogPost") 
    public BlogPost getBlogPost(){ 
     return new BlogPost(); 
    } 

    //List Blog Posts 
    @RequestMapping(value="/admin/blog", method=RequestMethod.GET) 
    public String blogAdmin(ModelMap map, SessionStatus status){ 
     status.setComplete(); 
     List<BlogPost> postList = blogService.getAllBlogPosts(); 
     map.addAttribute("postList", postList); 
     return "admin/blogPostList"; 
    } 

    //Add new blog post 
    @RequestMapping(value="/admin/blog/new", method=RequestMethod.GET) 
    public String newPost(ModelMap map){ 
     BlogPost blogPost = new BlogPost(); 
     map.addAttribute("blogPost", blogPost); 
     return "admin/editBlogPost"; 
    } 

    //Save new post 
    @RequestMapping(value="/admin/blog/new", method=RequestMethod.POST) 
    public String addPost(@Valid @ModelAttribute BlogPost blogPost, 
      BindingResult result, 
      @RequestParam("tagString") String tagString, 
      Model model, 
      SessionStatus status) 
    { 
     if (result.hasErrors()){ 
      return "admin/editBlogPost"; 
     } 
     else { 
      Set<Tag> tagSet = new HashSet(); 

      for (String tag: tagString.split(" ")){ 

       if (tag.equals("") || tag == null){ 
        //pass 
       } 
       else { 
        //Check to see if the tag exists 
        Tag tagObj = tagService.getTagByName(tag); 
        //If not, add it 
        if (tagObj == null){ 
         tagObj = new Tag(); 
         tagObj.setTagName(tag); 
         tagService.saveTag(tagObj); 
        } 
        tagSet.add(tagObj); 
       } 
      } 

      blogPost.setPostDate(Calendar.getInstance()); 
      blogPost.setTags(tagSet); 
      blogService.saveBlogPost(blogPost); 

      status.setComplete(); 

      return "redirect:/admin/blog"; 

     } 
    } 

    //Edit existing blog post 
    @Transactional 
    @RequestMapping(value="/admin/blog/{id}", method=RequestMethod.GET) 
    public String editPost(ModelMap map, @PathVariable("id") Integer postId){ 
     BlogPost blogPost = blogService.getBlogPostById(postId); 
     map.addAttribute("blogPost", blogPost); 
     Hibernate.initialize(blogPost.getTags()); 
     Set<Tag> tags = blogPost.getTags(); 
     String tagString = ""; 
     for (Tag tag: tags){ 
      tagString = tagString + " " + tag.getTagName(); 
     } 
     tagString = tagString.trim(); 
     map.addAttribute("tagString", tagString); 

     return "admin/editBlogPost"; 
    } 

    //Update post 
    @RequestMapping(value="/admin/blog/{id}", method=RequestMethod.POST) 
    public String savePostChanges(@Valid @ModelAttribute BlogPost blogPost, BindingResult result, @RequestParam("tagString") String tagString, Model model, SessionStatus status){ 
     if (result.hasErrors()){ 
      return "admin/editBlogPost"; 
     } 
     else { 
      Set<Tag> tagSet = new HashSet(); 

      for (String tag: tagString.split(" ")){ 

       if (tag.equals("") || tag == null){ 
        //pass 
       } 
       else { 
        //Check to see if the tag exists 
        Tag tagObj = tagService.getTagByName(tag); 
        //If not, add it 
        if (tagObj == null){ 
         tagObj = new Tag(); 
         tagObj.setTagName(tag); 
         tagService.saveTag(tagObj); 
        } 
        tagSet.add(tagObj); 
       } 
      } 
      blogPost.setTags(tagSet); 
      blogPost.setPostDate(Calendar.getInstance()); 
      blogService.updateBlogPost(blogPost); 

      status.setComplete(); 

      return "redirect:/admin/blog"; 

     } 
    } 

    //Delete blog post 
    @RequestMapping(value="/admin/delete/blog/{id}", method=RequestMethod.POST) 
    public @ResponseBody String deleteBlogPost(@PathVariable("id") Integer id, SessionStatus status){ 
     blogService.deleteBlogPost(id); 
     status.setComplete(); 
     return "The item was deleted succesfully"; 
    } 

    @RequestMapping(value="/admin/blog/cancel", method=RequestMethod.GET) 
    public String cancelBlogEdit(SessionStatus status){ 
     status.setComplete(); 
     return "redirect:/admin/blog"; 
    } 

} 

BlogPost.java

@Entity 
@Table(name="BLOG_POST") 
public class BlogPost implements Serializable { 

    @Id 
    @GeneratedValue(strategy=GenerationType.AUTO) 
    @Column(name="POST_ID") 
    private Integer postId; 

    @NotNull 
    @NotEmpty 
    @Size(min=1, max=200) 
    @Column(name="TITLE") 
    private String title; 

    ... 

    @ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL}) 
    @JoinTable(name="BLOG_POST_TAGS", 
     joinColumns={@JoinColumn(name="POST_ID")}, 
     inverseJoinColumns={@JoinColumn(name="TAG_ID")}) 
    private Set<Tag> tags = new HashSet<Tag>(); 

    ... 

    public Set<Tag> getTags() { 
     return tags; 
    } 

    public void setTags(Set<Tag> tags) { 
     this.tags = tags; 
    } 

} 

Tag.java

@Entity 
    @Table(name="TAG") 
    public class Tag implements Serializable { 

     @Id 
     @GeneratedValue(strategy=GenerationType.AUTO) 
     @Column(name="TAG_ID") 
     private Integer tagId; 

     @NotNull 
     @NotEmpty 
     @Size(min=1, max=20) 
     @Column(name="TAG_NAME") 
     private String tagName; 

     @ManyToMany(fetch = FetchType.LAZY, mappedBy="tags") 
     private Set<BlogPost> blogPosts = new HashSet<BlogPost>(); 

     public Integer getTagId() { 
      return tagId; 
     } 

     public void setTagId(Integer tagId) { 
      this.tagId = tagId; 
     } 

     public String getTagName() { 
      return tagName; 
     } 

     public void setTagName(String tag) { 
      this.tagName = tag; 
     } 

     public Set<BlogPost> getBlogPosts() { 
      return blogPosts; 
     } 

     public void setBlogPosts(Set<BlogPost> blogPosts) { 
      this.blogPosts = blogPosts; 
     } 


    } 
+0

首先,改變你的方法'addPost',讓參數列表中的'BlogPost'參數後面有'BindingResult'參數。 –

+0

@SotiriosDelimanolis:這個訂單爲什麼會有所作爲? – woemler

+0

這是一個春季大會。想象一下你有多個命令對象和它們相應的'BindingResult'。 –

回答

1

如果您選擇編碼在String作爲客戶端和服務器之間傳輸數據模型的標籤,你可能讓你的生活有點困難,如果你想以後提高你的UX 。

我會考慮將Set<Tag>作爲自己的模型元素,我會在JSON模型上使用JavaScript直接在前端進行轉換。

因爲我想有自動完成我的標記,我會通過現有的所有標籤爲/admin/blog/new模型,以紀念的能力,其標籤屬於博客文章的一部分(例如,作爲Map<Tag, Boolean>或兩套) - 最有可能使用JSON映射。我會在前端使用JavaScript來修改這個模型(也許利用一些提供一些很好的自動完成功能的jQuery插件),並依靠default JSON MappingJackson)進行反向轉換。

所以我的模型至少有兩個元素:博客帖子和所有標籤(一些標記爲「分配給此BlogPost」的標籤)我將使用TagService來確保存在所有相關標籤, where name in (<all assigned tag names>)並設置我的BlogPost.setTags(assignedTags)。

此外,我想有一些清理功能,從數據庫中刪除未使用的標籤。如果我想讓服務器變得更加簡單,那麼我會在另一個模型元素中刪除已刪除的標籤(這樣我可以檢查這是否是使用此標籤的最後一個BlogPost)。

+0

有趣的想法,我會給它一個鏡頭,看看它是否符合我的需求。謝謝! – woemler

+0

雖然不是我最初尋找的東西,但我確實朝着這個方向去解決問題,並對結果感到滿意。我將用我的最終代碼更新我的問題。 – woemler