JPA实体映射——多对一关系映射进一步学习

我们先看看第一种情况,如何持计划多对一的关系,以及如何删除这样的关系?

首先上代码:

public class ManyToOneTest extends AbstractTest {


    @Override
    protected Class[] entities() {
        return new Class[]{
                Post.class,
                PostComment.class,
        };
    }

    @Test
    public void testLifecycle() {
        //第一步先持久化一个Post实体
        doInJPA(entityManager -> {
            Post post = new Post()
                    .setId(1L)
                    .setTitle("First post");
            entityManager.persist(post);
        });
        //通过查询出主表post,然后持计划子表post_comment
        doInJPA(entityManager -> {
            Post post = entityManager.find(Post.class, 1L);

            entityManager.persist(
                    new PostComment()
                            .setId(1L)
                            .setReview("My first review")
                            .setPost(post)
            );
        });
        //解除与post的关系
        doInJPA(entityManager -> {
            PostComment comment = entityManager.find(PostComment.class, 1L);

            comment.setPost(null);
            entityManager.remove(comment);
        });
        //删除post_comment表的数据
        doInJPA(entityManager -> {
            PostComment comment = entityManager.getReference(PostComment.class, 1L);

            entityManager.remove(comment);
        });
    }

    @Entity(name = "Post")
    @Table(name = "post")
    public static class Post {

        @Id
        private Long id;

        private String title;

        public Long getId() {
            return id;
        }

        public Post setId(Long id) {
            this.id = id;
            return this;
        }

        public String getTitle() {
            return title;
        }

        public Post setTitle(String title) {
            this.title = title;
            return this;
        }
    }

    @Entity(name = "PostComment")
    @Table(name = "post_comment")
    public static class PostComment {

        @Id
        private Long id;

        private String review;

        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "post_id")
        private Post post;

        public Long getId() {
            return id;
        }

        public PostComment setId(Long id) {
            this.id = id;
            return this;
        }

        public String getReview() {
            return review;
        }

        public PostComment setReview(String review) {
            this.review = review;
            return this;
        }

        public Post getPost() {
            return post;
        }

        public PostComment setPost(Post post) {
            this.post = post;
            return this;
        }
    }
}

首先分析一下上面的代码:主表显然是post,主键是post表的id;子表是post_detail,外键是post_id;

有了以上分析,我们可以看出,持久化主表post是最简单的,直接调用entityManager.persist()就可以了,而持久化子表就不那么容易,需要建立表外键关系,就是查询出post,然后设置到子表的post属性中,然后才持计划。

同理,在删除的时候,你不能直接删除post表,因为它的主键被post_detail的外键引用;需要先删除post_detail后才能删除;而删除post_detail的时候,需要先解除外键关系,然后再删除。

我们看看以上测试执行的SQL:

["insert into post (title, id) values (?, ?)"], Params:[(First post,1)]
["select manytoonet0_.id as id1_0_0_, manytoonet0_.title as title2_0_0_ from post manytoonet0_ where manytoonet0_.id=?"], Params:[(1)]
["insert into post_comment (post_id, review, id) values (?, ?, ?)"], Params:[(1,My first review,1)]
["select manytoonet0_.id as id1_1_0_, manytoonet0_.post_id as post_id3_1_0_, manytoonet0_.review as review2_1_0_ from post_comment manytoonet0_ where manytoonet0_.id=?"], Params:[(1)]
["update post_comment set post_id=?, review=? where id=?"], Params:[(NULL(BIGINT),My first review,1)]
["select manytoonet0_.id as id1_1_0_, manytoonet0_.post_id as post_id3_1_0_, manytoonet0_.review as review2_1_0_ from post_comment manytoonet0_ where manytoonet0_.id=?"], Params:[(1)]
["delete from post_comment where id=?"], Params:[(1)]

好了,从以上代码可以看出在这样的多对一的映射下,该如何操作了吧?同时您也可以看到,是不是有三个不必要的查询呢?这里是否可以优化呢?

 

我们看看下面的代码:

@Test
public void testThreePostComments() {
    doInJPA(entityManager -> {
        Post post = new Post()
            .setId(1L)
            .setTitle("First post");
        entityManager.persist(post);
    });
    doInJPA(entityManager -> {
        Post post = entityManager.getReference(Post.class, 1L);

        entityManager.persist(
            new PostComment()
                .setId(1L)
                
                .setReview("My first review")
                .setPost(post)
        );

        entityManager.persist(
            new PostComment()
                .setId(2L)
                .setReview("My second review")
                .setPost(post)
        );

        entityManager.persist(
            new PostComment()
                .setId(3L)
                .setReview("My third review")
                .setPost(post)
        );
    });

    doInJPA(entityManager -> {
        PostComment comment1 = entityManager.getReference(PostComment.class, 2L);

        entityManager.remove(comment1);
    });

    doInJPA(entityManager -> {
        List comments = entityManager.createQuery(
            "select pc " +
            "from PostComment pc " +
            "where pc.post.id = :postId", PostComment.class)
            .setParameter("postId", 1L)
            .getResultList();

        assertEquals(2, comments.size());
    });
}

这个例子和上一个例子最大的区别是,在持久化子表post_detail的时候,查询的时候使用了entityManager.getReference(),使用这个方法的好处是避免了对主表post的查询,从而少了一次查询。我们看看日志:

["insert into post (title, id) values (?, ?)"], Params:[(First post,1)]
["insert into post_comment (post_id, review, id) values (?, ?, ?)"], Params:[(1,My first review,1)]
["insert into post_comment (post_id, review, id) values (?, ?, ?)"], Params:[(1,My second review,2)]
["insert into post_comment (post_id, review, id) values (?, ?, ?)"], Params:[(1,My third review,3)]
["select manytoonet0_.id as id1_1_0_, manytoonet0_.post_id as post_id3_1_0_, manytoonet0_.review as review2_1_0_ from post_comment manytoonet0_ where manytoonet0_.id=?"], Params:[(2)]
["delete from post_comment where id=?"], Params:[(2)]
["select manytoonet0_.id as id1_1_, manytoonet0_.post_id as post_id3_1_, manytoonet0_.review as review2_1_ from post_comment manytoonet0_ where manytoonet0_.post_id=?"], Params:[(1)]

从日志我们可以看出,在插入子表部分进行了优化,当然,如果这里可以批量插入,那么这个优化就太棒了,不知道有没有啥好的办法做到批量插入子表;同时这里的删除如果能直接删除不用查询就更好了。

 

 

版权声明:著作权归作者所有。

thumb_up 0 | star_outline 0 | textsms 0