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