Spring + Hibernate:多对多关系和Web表单

时间:2013-10-01 00:28:35

标签: java forms hibernate spring-mvc jpa

我一直在努力解决如何在我使用Spring 3和Hibernate 4构建的Web应用程序中实现创建多对多关系的表单。我正在尝试使用标记系统构建一个简单的博客工具。我创建了一个与模型BlogPost具有多对多关系的模型Tags。当我创建新的BlogPost对象时,标签的Web表单输入是单行文本输入。我希望能够用空格分割这个文本字符串,并用它来创建Tag个对象。或者,在修改现有BlogPost时,我希望能够获取与Set相关联的Tag BlogPost个对象并将其转换为{{1}用作input元素的值。我的问题是使用我的表单在文本输入和引用的String对象集之间进行转换。

绑定/获取/更新与Web表单的多对多关系的最佳实践是什么?有一种简单的方法可以做到这一点,我不知道吗?

更新

我按照下面的答案建议,手动处理表单中Tag标记值与对象模型所需的String对象之间的对象转换。这是最终的工作代码:

editBlogPost.jsp

Set<Tag>

BlogController.java

...
<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>
....

BlogPost.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";
    }

}

Tag.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;
    }

}

2 个答案:

答案 0 :(得分:1)

如果您选择在String中将标签编码为客户端和服务器之间的传输数据模型,那么如果您希望以后改进用户体验,可能会让您的生活更加困难。

我会考虑将Set<Tag>作为自己的模型元素,我会在JSON模型上使用JavaScript直接在前端进行转换。

由于我希望自动完成标记,因此我会将所有现有标记作为/admin/blog/new模型的一部分传递,并能够标记哪些标记属于博客帖子(例如{{1}或者两个集合) - 最有可能使用JSON映射。我会在前端使用JavaScript修改此模型(可能使用一些提供一些不错的自动完成功能的jquery插件)并依赖default JSON MappingJackson)进行反向转换。

所以我的模型至少有两个元素:博客帖子和所有标签(一些标记为“分配给此BlogPost”。我会使用TagService来确保所有相关标签的存在,用{查询它们} {1}}并设置我的BlogPost.setTags(assignedTags)。

另外我想要一些清理功能来从DB中删除未使用的标签。如果我想让服务器更容易,我会有另一个带有删除标签的模型元素(所以我可以检查这是否是使用此标签的最后一个BlogPost)。

答案 1 :(得分:0)

这应该以您的形式工作:

<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>