当前位置: 首页 > 知识库问答 >
问题:

性能hibernate @子选择与数据库视图

上官凯歌
2023-03-14

我有一个基于Java8 Spring Boot 2.3.3的应用程序(使用Hibernate5.4.20),我有一个Postgreql。我想最终了解使用数据库视图和@Sub选择是否更好(对于性能)。

简单概述一下:我有一个实体“Book”和3个实体“BookRank”(用户给书打1到10颗星)、“BookComment”(用户对书的评论)、“BookLike”(用户把书的评论放在一起),每个实体都有Book关系(ManyToOne),所以在我的查询结果中,我想这样对书进行排序:

order by avg(book_rank) desc, sum(book_rank) desc, count(book_comment) desc, count(book_like) desc

当然有很多书,很多排名,评论和喜欢...:)

我找到了4种方法来做同样的事情,从一个复杂的查询开始,里面有3个子选择(这对我来说是最好的解决方案,但如果有人有更好的方法,请告诉我)。

select *
from(
    select bbb.id as book_id, min(bbb.b_rank_average) as b_rank_average, min(bbb.b_rank_sum) as b_rank_sum, min(bbb.b_comment_count) as b_comment_count, count(b_l.id) as b_like_count
    from(
        select rr.id, min(bb.b_rank_average) as b_rank_average, min(bb.b_rank_sum) as b_rank_sum, count(b_c.id) as b_comment_count
        from(           
            select b.id,  avg(b_r.rank) as b_rank_average, sum(b_r.rank) as b_rank_sum
            from book as b
            left join book_rank as b_r on (b.id = b_r.book_id and b_r.deleted = false)
            group by b.id) as bb
            
        left join book_comment as b_c on (bb.id = b_c.book_id and b_c.deleted = false)
        group by bb.id) as bbb
                
    left join book_like as b_l on (bbb.id = b_l.book_id and b_l.deleted = false)
    group by bbb.id
    ) as bbbb
    
left join book on bbbb.book_id = book.id
where book.deleted = false
order by b_rank_average desc nulls last, b_rank_sum desc nulls last, b_comment_count desc, b_like_count desc, book_id desc;

我想知道哪一个是最好的表演。。。

1) 存储库中的纯本机查询-无视图-无不可变实体

在我的存储库中,我只有一个包含上面写的原生查询的方法

2) 在存储库中使用数据库视图和本机查询

使用查询的中心部分创建数据库视图:

create or replace view book_ranking as

    select bbb.id as book_id, min(bbb.b_rank_average) as b_rank_average, min(bbb.b_rank_sum) as b_rank_sum, min(bbb.b_comment_count) as b_comment_count, count(b_l.id) as b_like_count
    from(
        select rr.id, min(bb.b_rank_average) as b_rank_average, min(bb.b_rank_sum) as b_rank_sum, count(b_c.id) as b_comment_count
        from(           
            select b.id,  avg(b_r.rank) as b_rank_average, sum(b_r.rank) as b_rank_sum
            from book as b
            left join book_rank as b_r on (b.id = b_r.book_id and b_r.deleted = false)
            group by b.id) as bb
            
        left join book_comment as b_c on (bb.id = b_c.book_id and b_c.deleted = false)
        group by bb.id) as bbb
                
    left join book_like as b_l on (bbb.id = b_l.book_id and b_l.deleted = false)
    group by bbb.id

并修改存储库方法中的本机查询以使用视图:

select * 
from book_ranking as the_view
left join book on the_view.book_id = book.id 
where book.deleted = false
order by b_rank_average desc nulls last, b_rank_sum desc nulls last, b_comment_count desc, b_like_count desc, book_id desc;

3)具有数据库视图和不可变的实体,该实体在存储库中选择视图和jpql查询

使用实体(不可变)封装上面的视图

@Entity
@Subselect("select * from book_ranking")
public class BookRanking implements Serializable {
    ....
}

以及修改连接图书实体和新图书排名不变实体(即视图)的存储库查询(非本机)

@Query("select b from BookRanking as b_r 
        join Book b on b_r.bookId = b.id 
        where b.deleted = false
        order by b_r.b_rank_average desc nulls last, b_r.b_rank_sum desc nulls last, b_r.b_comment_count desc, b_r.b_like_count desc, b_r.bookId desc)

4)没有视图,但不可变的实体在@subselect中有查询,在存储库中有jpql查询

数据库上没有视图,但不可变实体 BookRanking 在@Subselect注释中使用“视图查询”和如下所示的@Synchronize注释进行查询:

@Entity
@Subselect("select bbb.id as book_id, min(bbb.b_rank_average) as b_rank_average, min(bbb.b_rank_sum) as b_rank_sum, min(bbb.b_comment_count) as b_comment_count, count(b_l.id) as b_like_count
    from(
        select rr.id, min(bb.b_rank_average) as b_rank_average, min(bb.b_rank_sum) as b_rank_sum, count(b_c.id) as b_comment_count
        from(           
            select b.id,  avg(b_r.rank) as b_rank_average, sum(b_r.rank) as b_rank_sum
            from book as b
            left join book_rank as b_r on (b.id = b_r.book_id and b_r.deleted = false)
            group by b.id) as bb
            
        left join book_comment as b_c on (bb.id = b_c.book_id and b_c.deleted = false)
        group by bb.id) as bbb
                
    left join book_like as b_l on (bbb.id = b_l.book_id and b_l.deleted = false)
    group by bbb.id )

@Synchronize({ "book", "book_rank", "book_comment", "book_like" })

public class BookRanking implements Serializable {
    ....
}

并使用相同的存储库查询(不是本机)加入第3点的Book实体和新的BooRanking不可变实体(类似于视图)

共有1个答案

柳和怡
2023-03-14

一般来说,数据库视图只是一个关系,通常在解析期间被扩展到查询计划中,因此在使用视图和直接写出整个查询时应该没有明显的区别。使用视图可以使您更容易地重用查询,但当您想要更改某些内容时,您必须更改视图,可能还必须更改所有使用视图的应用程序,因此重用性方面会让您反感。

我通常不推荐视图,因为我看到人们在视图中加入了很多不必要的连接,唯一的目的就是“让别人更容易”。未使用的联接的问题是,数据库通常无法消除它们。TLDR,我建议直接在代码中编写查询,因为您可以省略不需要的连接,从而获得更好的性能。

您可以使用以下更简单的查询:

select 
    b.id, 
    avg(b_r.rank) as b_rank_average, 
    sum(b_r.rank) as b_rank_sum,
    (select count(*) from book_comment as b_c where b.id = b_c.book_id and b_c.deleted = false) as b_comment_count
    (select count(*) from book_like as b_l where b.id = b_l.book_id and b_l.deleted = false) as b_like_count
from book as b
left join book_rank as b_r      on (b.id = b_r.book_id and b_r.deleted = false)
where b.deleted = false
group by b.id
order by b_rank_average desc nulls last, b_rank_sum desc nulls last, b_comment_count desc, b_like_count desc, b.id desc

其也可以用JPQL/HQL查询建模。它看起来非常相似:

select 
    b.id, 
    avg(r.rank) as b_rank_average, 
    sum(r.rank) as b_rank_sum,
    (select count(*) from b.comments c where c.deleted = false) as b_comment_count
    (select count(*) from b.booksLike l where l.deleted = false) as b_like_count
from book as b
left join b.ranks as r on r.deleted = false
where b.deleted = false
group by b.id
order by b_rank_average desc nulls last, b_rank_sum desc nulls last, b_comment_count desc, b_like_count desc, b.id desc
 类似资料:
  • 问题内容: 我有一个称为Data的数据库,它是数据库中每个实体的父级。它包含Id等通用属性。然后,我有了一个扩展Data的实体,这也是由于其子类的通用功能而引起的。我的数据库中的映射是正确的。 这是我的等级制度的一个例子 并且表已正确映射: 无论如何,是否在数据库中查询所有作为Employee实例的Employee子类(FullTimeEmployee,PartTimeEmployee),而不在查

  • 问题内容: Hibernate提供(至少)两个选项来解决N + 1查询问题。一个是将FetchMode设置为Subselect,这会生成一个带有IN子句的选择以及此IN子句中的子选择。另一个是指定BatchSize,它会生成一个带有包含父母ID的IN子句的选择。 两者都可以工作,但是我发现由于对父项的查询很复杂,Subselect选项经常遇到性能问题。另一方面,对于大的BatchSize(例如10

  • 我有一个带有spring 3和hibernate框架的标准web应用程序。我有两个应用程序上下文。xml和hibernate。cfg。包含数据库连接数据的xml文件: ... 现在,我需要使用动态数据库名称更改这个应用程序,如何在ServletContextListener中设置它的运行时。contextInitialized?

  • 问题内容: 从MySQL中的多个表中选择时,以下两个查询均返回相同的结果集。 这些查询中的一个比另一个查询 更好 或更有效吗?根据我在一个小的数据集(每个表中约有2000行)的测试中,它们都在大约相同的执行时间上返回了相同的结果集。 查询1: 查询2: 问题答案: 它们是相同的,但是语法不同。因此,您不应该期望这两种语法之间的性能差异。但是,建议使用最后一种语法(ANS SQL-92语法),有关更

  • 命令用于选择数据库,如果想在一个数据库上工作,比如:创建表,查询表,更新,创建存储过程等等,那么首先需要选择一个目标数据库。 示例 假设在MariaDB数据库服务器中,存在有多个数据库,我们必须选择一个特定的数据库。 例如,在下图中显示了多个数据库: 这里我们将使用数据库来创建表等。所以需要使用以下命令。 在执行上面查询语句后,就已经选择数据库。之后就可以在里面创建表等数据对象了。执行上面查询语句

  • 上一章节我们讲了如何创建数据库,接下来我们来讨论如何去选择我们创建的数据库。 数据库的命令窗口 PostgreSQL 命令窗口中,我们可以命令提示符后面输入 SQL 语句: 使用 \l 用于查看已经存在的数据库: 接下来我们可以使用 \c + 数据库名 来进入数据库: 系统命令行窗口 在系统的命令行查看,我么可以在连接数据库后面添加数据库名来选择数据库: pgAdmin 工具 pgAdmin 工具