如何通过在Hibernate中引入二级缓存来解决N + 1问题?

J.O*_*sen 6 java caching hibernate jpa second-level-cache

在Hibernate文档的性能部分中说明:

对N + 1选择问题采用完全不同的方法是使用二级缓存.

我不明白它是如何解决问题的.什么可能是现实世界的例子和解释?

Vla*_*cea 3

这很简单。假设您有以下域模型:

@Entity(name = "Post")
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "post")
    private List<Comment> comments = new ArrayList<>();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Comment> getComments() {
        return comments;
    }

    public void addComment(Comment comment) {
        comments.add(comment);
        comment.setPost(this);
    }
}

@Entity(name = "Comment")
public class Comment {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne
    private Post post;

    public Comment() {
    }

    public Comment(String review) {
        this.review = review;
    }

    private String review;

    public Long getId() {
        return id;
    }

    public Post getPost() {
        return post;
    }

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

    public void setReview(String review) {
        this.review = review;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果运行以下 HQL 查询:

List<Comment> comments = session.createQuery(
    "select c from Comment c ").list();
for(Comment comment : comments) {
    Post post = comment.getPost();
}
Run Code Online (Sandbox Code Playgroud)

然后,对于每条评论,您都必须运行额外的查询来获取关联的评论帖子。

如果启用二级缓存:

@Entity(name = "Post")
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Post {
    ...
}
Run Code Online (Sandbox Code Playgroud)

然后Hibernate首先去二级缓存加载实体,只有在没有找到缓存条目的情况下才会访问数据库。

一个更简单的解决方案是在查询时简单地获取所有必需的数据

List<Comment> comments = session.createQuery(
    "select c from Comment c fetch c.post ").list();
Run Code Online (Sandbox Code Playgroud)

这样您就不会遇到 N+1 查询问题,也不需要二级缓存。与任何缓存解决方案一样,当数据库在 Hibernate API 外部更新时,二级缓存很容易出现不一致。