簡體   English   中英

Spring + Hibernate:多對多關系和 Web 表單

[英]Spring + Hibernate: Many-to-Many relationships and web forms

我一直在努力研究如何在使用 Spring 3 和 Hibernate 4 構建的 Web 應用程序中實現創建多對多關系的表單。我正在嘗試構建一個帶有標記系統的簡單博客工具。 我創建了一個模型BlogPost ,它與模型Tags具有多對多關系。 當我創建一個新的BlogPost對象時,標簽的 Web 表單輸入是單行文本輸入。 我希望能夠通過空格分割此文本字符串並使用它來創建Tag對象。 或者,在編輯現有的BlogPost ,我希望能夠獲取與BlogPost關聯的Tag對象Set並將其轉換為用作輸入元素值的String 我的問題是使用我的表單在文本輸入和引用的Tag對象集之間進行轉換。

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

更新

正如下面的答案所建議的那樣,我決定手動處理表單中的String標記值與對象模型所需的Set<Tag>對象之間的對象轉換。 這是最終的工作代碼:

編輯BlogPost.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>
....

博客控制器.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";
    }

}

博客帖子.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;
    }

}

標簽.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;
        }


    }

如果您選擇將您的標簽編碼為String作為客戶端和服務器之間的傳輸數據模型,如果您以后想改進您的用戶體驗,那么您的生活可能會變得更加艱難。

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

由於我希望為我的標記自動完成,我會將所有現有標簽作為/admin/blog/new模型的一部分傳遞,並能夠標記哪些標簽屬於博客文章(例如作為Map<Tag, Boolean>或兩個 Sets) - 最有可能使用 JSON 映射。 我會在前端使用 JavaScript 修改這個模型(也許使用一些提供一些很好的自動完成功能的 jquery 插件)並依賴默認的 JSON 映射( Jackson ) 進行反向轉換。

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

此外,我想要一些清理功能來從數據庫中刪除未使用的標簽。 如果我想讓服務器更容易,我將有另一個模型元素,其中刪除了已刪除的標簽(因此我可以檢查這是否是最后一個使用此標簽的 BlogPost)。

這應該以您的形式工作:

<div class="form-check">
    <input class="form-check-input" type="checkbox" value="1"
        name="categories"> <label class="form-check-label"
        for="categories"> Cat 1 </label> 

    <input class="form-check-input"
        type="checkbox" value="2" name="categories"> <label
        class="form-check-label" for="categories"> Cat 2 </label>
</div>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM