使用地图的JPA集合

时间:2013-04-05 18:23:47

标签: java spring hibernate jpa spring-data

所以我使用JPA,Hibernate和Spring Data进行了一个小概念验证。在这个POC中,我创建了一个Post实体和一个Comment实体。 Post实体包含评论地图Map<Integer,Comment>

由于我使用Comment实体Map列作为地图的关键字,因此我遇到了将多个Comment实体持久存储到@Id的问题。基本上,如果我没有明确设置Comment Hibernate的id将它们识别为同一个实体并且只保留其中一个。

我发现我可以通过指定垃圾密钥来持久化Map,MySql自动编号会在持久化后替换。这导致了一个问题,当前映射中的键不会收到数据库设置的新commentId值。这导致我的单元测试失败。

所以基本上我有三个问题?

  1. 我应该指定垃圾密钥来保存地图中的实体吗?
  2. 我的方法都错了吗?如果是这样,最好的方法是什么?
  3. 如果垃圾密钥正常,我怎样才能在插入后刷新它们?
  4. Post.java

    @Entity
    @Table(name = "POST")
    public class Post {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "POST_ID")
        private Integer postId;
    
        @Column(name = "TITLE")
        private String title;
    
        @OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
        @MapKeyColumn(name="COMMENT_ID")
        private Map<Integer, Comment> comments = new HashMap<Integer, Comment>();
    
            /** Accessors (Get/Set) **/
            /** HashCode & Equals that does not consider the postId field or the Map */
    }
    

    Comment.java

    @Entity
    @Table(name="COMMENT")
    public class Comment {
    
        @Id
        @Column(name="COMMENT_ID")
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        private Integer commentId;
    
        @Column(name="BODY")
        private String body;
    
        @ManyToOne(cascade=CascadeType.ALL)
        @JoinColumn(name="POST_ID")
        private Post post;
    
            /** Accessors (Get/Set) **/
            /** HashCode & Equals that does not consider the commentId field or the Post */
    }
    

    单元测试

    @Test
    public void insertTest2(){
        Comment comment = new Comment();
        Comment comment2 = new Comment();
        final String body1 = "This is a map test";
        final String body2 = "This is another map test";
    
        comment.setBody(body1);
        comment2.setBody(body2);
    
        Post post = new Post();
        post.setPostDate(new Date());
        post.setTitle("First Post");
    
    
        post.getComments().put(1,comment);   //Setting garbage IDs
        post.getComments().put(2, comment2); //Setting garbage IDs
    
        comment.setPost(post);
        comment2.setPost(post);
    
        repository.save(post);
    
        Post dbpost = repository.findOne(post.getPostId());
    
        Map<Integer, Comment> comments = dbpost.getComments();
    
        //Test fails using old id       
        assertTrue(comments.containsKey(comment.getCommentId()));
        //Same here 
        assertTrue(comments.containsKey(comment2.getCommentId()));  
    
    }
    

    另请注意,这是一种双向关系

1 个答案:

答案 0 :(得分:2)

  

我应该指定垃圾密钥来保存地图中的实体吗?

没有。一旦真实ID受到影响,地图就会处于不一致状态。地图的键应该是不可变的。

  

我的方法都错了吗?如果是这样,最好的方法是什么?

坦率地说,我并没有真正看到使用地图按ID保存评论的重点。实体经理已经能够通过ID获得评论。我只想使用Set或List。

另一种方法是在将注释添加到地图之前保留注释并刷新实体管理器,以确保它们已经具有真实ID。