为什么JDBC不支持批量获取数据?

时间:2015-08-03 10:30:53

标签: java performance jdbc

JDBC使用addBatchexecuteBatch长期支持批量更新。为什么没有任何支持添加一堆预准备语句并将结果集数组作为响应?

例如,如果我想为单个视图加载客户详细信息,基本帐户详细信息,基本卡详细信息,基本贷款详细信息等,我更愿意创建一堆预准备语句并将准备好的语句附加到ArrayList并作为批处理执行它们。然后我将遍历结果集并处理数据。希望能保存几次网络往返(假设我的查询是高效的)。

大量查询示例:

SELECT custid, first, last, age FROM Customer where custid = ?
SELECT custid, acno, accountname, accounttype, status FROM Account where custid = ?
SELECT custid, cardno, cardname, cardtype, status FROM CreditCard where custid = ?
SELECT custid, loanno, principal, rate FROM Loan where custid = ?

我可以想象为什么它可能是一个坏主意的几个假设理由。但是,我不确定哪个在现实世界中最可能是真的。

反对批量获取的假设原因:

  1. 存在一些基本的网络/数据库堆栈/内存相关问题 这可以防止一堆选择查询在同一个上执行 连接和结果集保持打开状态。
  2. 响应处理代码过于繁琐,因为在调用级别和单个语句级别可能存在异常。并且,必须正确关闭几个陈述。
  3. 在减少网络呼叫数量方面没有显着的性能提升。查询执行是主要瓶颈,网络往返成本微不足道。
  4. 可能会滥用此类功能。与其他查询一样批量处理的单个非性能查询可能导致应用程序执行效果不佳。
  5. 我问这个的原因是因为我经常看到很多Join查询将父子关系合并到一个SQL查询中,只是为了在一次调用中完成加载。

    但是,随着表的数量增加,查询变得复杂。此外,在每个孩子的每一行中重复父表信息。因此,单个连接结果集中存在大量数据冗余。

    示例连接查询:

    SELECT custid, first, last, age, acno, accountname, accounttype, a.status, cardno, cardname, cardtype, c.status, loanno, principal, rate
    FROM Customer cc, Account a, CreditCard c, Loan l 
    WHERE a.custid=CC.custid(+) and c.custid=CC.custid(+) and l.custid=CC.custid(+)
    

4 个答案:

答案 0 :(得分:1)

如何将查询放入过程然后使用CallableStatement执行该过程?

  

CallableStatement可以返回一个ResultSet对象或多个   ResultSet对象。使用多个ResultSet对象进行处理   从Statement继承的操作。

  try 
  {
      CallableStatement stmt = con.prepareCall(/* call procedure */);       

      boolean results = stmt.execute();
      int rsCount = 0;    

      while (results) 
      {
           ResultSet rs = stmt.getResultSet();

           while (rs.next()) 
           {

           }
           rs.close();    
        results = stmt.getMoreResults();
      } 
      stmt.close();
   }
   catch (Exception e) {
      e.printStackTrace();
   }

答案 1 :(得分:1)

JDBC API 支持此功能。

Statement.getMoreResults()可以告诉您通过execute()执行的SQL语句是否产生了多个ResultSet

引用来自JavaDocs的getMoreResults()

  

移动到此Statement对象的下一个结果,如果它是ResultSet对象,则返回true,并隐式关闭使用getResultSet方法获得的任何当前ResultSet对象。

     

如果满足以下条件,则没有更多结果:

// stmt is a Statement object<br>
((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))

然而,它取决于后端DBMS和JDBC驱动程序,如果,您可以使用它。有些JDBC驱动程序拒绝使用单个execute()调用运行多个语句(主要是作为防止SQL注入的方法),其他人则不这样做。

所以在例如Postgres你可以做这样的事情:

boolean hasResult = stmt.execute(
  "select * from table_1;\n" +
  "select * from table_2;");

while (hasResult) 
{
  rs = stmt.getResultSet();
  while (rs.next()) 
  {
    // process the result set
  }
  hasResult = stmt.getMoreResults();
}

这甚至允许混合SELECTUPDATE语句如果您还要检查getUpdateCount()

据我所知,您也可以使用SQL Server执行此操作。它不适用于Oracle。

我还没试过PreparedStatement。但是,getMoreResults()Statement定义时,它也适用于PreparedStatement

答案 2 :(得分:0)

关系数据库经过设计和优化,可通过SQL查询检索数据,JOIN来自多个表的数据。执行单个查询(正确)JOIN数据可能总是比使用单独查询获取相同数据更有效。

如果单个查询过于复杂,则应将其重构为VIEW - 您可以从中查询,加入来自其他TABLEVIEW的数据(如果需要)

鉴于上述情况,我认为不需要批量查询。

答案 3 :(得分:0)

我觉得你不明白准备好的陈述是什么。

预准备语句是您声明一次的对象,然后一直使用不同的提供参数重用它。

您是否一直没有告诉我,每次您希望再次执行时,都会从头开始重新创建准备好的声明?

假设您有四个循环。在执行循环之前,您可以执行以下操作:

 PreparedStatement statement1, statement2, statement3,statement4;
 try {
        con.setAutoCommit(false);//only needed when also doing updates/inserts
        statement1 = con.prepareStatement("SELECT custid, first, last, age FROM  Customer where custid = ?");
        statement2 = con.prepareStatement("SELECT custid, acno, accountname, accounttype, status FROM Account where custid = ?");
        // etc....
        for (Map.Entry<String, Integer> e : customers.entrySet()) {
            statement1.setInt(1, e.getValue().intValue());
            ResultSet rs = statement1.executeQuery();
            // do what you need to do
            statement2.setInt(1, e.getValue().intValue());
            ResultSet rs2 = statement2.executeQuery();
            // do what you need to do
         }
         con.commit();//only needed when also doing updates/inserts
     }

   }

无需重新创建准备好的语句。这就是为什么它被称为准备好的声明。您只需为其提供查询所需的新值。

通过这种方式,您可以将其添加到列表中,以您希望迭代它的方式迭代它等等。并且它已全部优化,因为数据库引擎将记住查询计划及其为其创建的优化。您对准备好的语句对象的处理取决于您。

如果你不断重新创建对象,它也会这样做,因为它会记住查询,但是你可以节省一遍又一遍地创建新对象的开销以及随之而来的内存问题。

所以,如果没有更明确的问题,这是我能给你的最佳答案。