【问题标题】:MyBatis RowBounds doesn't limit query resultsMyBatis RowBounds 不限制查询结果
【发布时间】:2021-01-05 19:36:38
【问题描述】:

我正在开发一个需要支持分页的无状态 API。

我使用 Oracle 数据库。 我使用 Spring 和 MyBatis 进行数据库访问。

从文档中,我了解到我可以使用 RowBounds 类来限制查询返回的行数。

但是,似乎没有对查询进行特殊优化以支持分页。

例如,如果我将 RowBounds 设置为偏移 100 并包含 50 条记录,我希望查询添加以下内容:

(original query with the where clause...)
and ROWNUM < 150
and ROWNUM >= 100

但是那里什么都没有,只是我手动定义的查询。

这对性能来说很糟糕,因为我可能有几千个结果。

我做错了什么?

谢谢。

【问题讨论】:

    标签: sql spring oracle mybatis


    【解决方案1】:

    Mybatis 将许多事情留给正在使用的SQL driver,看来RowBounds 周围的确切行为就是其中之一。

    请参阅http://mybatis.github.io/mybatis-3/java-api.html,尤其是以下部分:

    不同的驱动程序能够达到不同的效率水平 在这方面。为了获得最佳性能,请使用结果集类型 SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE(换句话说:不是 FORWARD_ONLY)。

    默认值显然是UNSET,但您可以尝试在select 标签中使用SCROLL_SENSITIVE 作为ResultSetType 属性,看看是否有帮助。有关更多信息,请参阅http://mybatis.github.io/mybatis-3/sqlmap-xml.html

    如果这不起作用,您始终可以通过放弃使用 RowBounds 并实现一个 SettingsBean 类(或类似的),您的 select 标记将作为parameterType,其中包含offsetlimit 的字段(或者rowStartrowEndOracle 更有意义,然后您可以根据需要在运行时设置它们并将它们动态插入到执行 select 时的 SQL。

    虽然需要更多代码,但您可以通过纯动态 SQL 完全按照自己的意愿控制行为。我在MybatisPostgres 中使用了这样的方法,并且效果很好。

    因此,您将使用这些字段及其 getter 和 setter 实现您的 SettingsBean 类,然后您的 select 语句可能类似于:

    <select
      id="selectFoo"
      parameterType="com.foo.bar.SettingsBean">
    
    select *
    from foo
    where rownum >= #{rowStart}
      and rownum < #{rowEnd}
    </select>
    

    【讨论】:

    • 谢谢。似乎 RowBounds 永远不会使用 Oracle rownum 技术。另外,resultSetType 设置似乎只与有状态的基于 Web 的应用程序相关,例如显示分页结果的网页。我在 Spring 和 MyBatis 中使用无状态 EJB,所以我认为它会对我的情况产生 0 的影响?似乎我将不得不重新使用 rownum 解决方案。我真的认为 MyBatis 团队应该更清楚地传达 RowBounds 的局限性。他们将其描述为一种简单的抽象分页方式,而实际上它的用途非常有限。
    • 当然,没问题。理论上,即使在无状态应用程序中,它也可以工作——只需要使用不同的offsets 和limit 进行顺序查询。然而,这实际上取决于Oracle 驱动程序如何处理它,因为一次获取​​的行数取决于JDBC 层,而后者又对各个驱动程序有一堆依赖(Oracle,@ 987654348@等)。我同意它比它更简单。我发现Mybatis 也适用于更复杂类型的映射器,例如字符串数组。
    【解决方案2】:

    我找到了解决此问题的简单方法。我遵循了 @khampson 推荐的 Mybatis 指令,并将一个 RowBounds 实例传递给映射器,没有强制执行任何限制。

    RowBounds rowbounds = new RowBounds(0, resultLimit);
    roster = tableMapper.selectAll(rowbounds);
    

    映射器java

    public List<Row> selectAll(RowBounds rowbounds);
    

    映射器 xml

    <select id="com.TableMapper.selectAll" resultMap="row" timeout="10">
                SELECT * FROM table;
    </select>
    

    只需将“LIMIT #{param1.offset}, #{param1.limit}”附加到映射器的 xml 即可产生我想要的行为!

    <select id="com.TableMapper.selectAll" resultMap="row" timeout="10"> 
        SELECT * FROM table LIMIT #{param1.offset}, #{param1.limit}; 
    </select>
    

    【讨论】:

      【解决方案3】:

      我在 Postgres 数据库 上进行了测试,并从 AngularUI 进行了分页。第一页为N°1,调用N°0页,服务返回所有数据。

      java 服务:

      public List<Foo> selectAll(int currentPage, int itemsPerPage);
          int offset = (currentPage - 1) * itemsPerPage;
          RowBounds rowbounds;
          if(currentPage == 0){
              rowBounds = new RowBounds();
          } else {
              rowBounds = new RowBounds(currentPage, itemsPerPage);
          }
          return fooMapper.selectAll(rowBounds);
      }
      

      java 映射器:

      public List<Foo> selectAll(RowBounds rowbounds);
      

      xml 映射器:

      <select id="selectAll" resultMap="Foo">
          SELECT * FROM foo;
      </select>
      

      【讨论】:

        【解决方案4】:

        Rownum 是 Oracle 中的伪列,这意味着它在最后阶段被添加到结果集中。这意味着,将rownum 与大于 1 的任何值进行比较总是不返回任何记录。有 2 个选项可以修改您的 SQL 语句:

        1. 将 rownum 包装为嵌套查询:
        select * 
          from (
                select a.*, ROWNUM rnum 
                  from .... a
                 where ...
                   and ROWNUM <=  :max_rows
                 order by -- important 
               )
         where rnum  >= :offset
        

        其中:max_rowsoffset + page size

        1. 使用解析函数:
          select *
            from (
                    select a.*, ROW_NUMBER() OVER (ORDER BY id) rnum
                      from ....
                     -- order by is not important here
                 )
           where rnum between 30 and 50;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-09-28
          • 1970-01-01
          • 1970-01-01
          • 2013-07-06
          • 2014-08-04
          • 2016-04-01
          • 2011-01-17
          • 2020-10-07
          相关资源
          最近更新 更多