MySQL ResultSet 可滚动/可更新未按预期工作

Posted

技术标签:

【中文标题】MySQL ResultSet 可滚动/可更新未按预期工作【英文标题】:MySQL ResultSet scrollable/updatable not working as expected 【发布时间】:2014-07-12 03:04:25 【问题描述】:

我有一个测试 JDBC 程序,它试图改变 ResultSet 的可滚动性和可更新性特性。不幸的是,TYPE_CONCUR_ 的所有组合似乎都会产生相同的结果(TYPE_SCROLL_INSENSITIVECONCUR_READ_ONLY)。

即使使用默认值 (TYPE_FORWARD_ONLY),也可以滚动浏览 ResultSet。谁能解释这是为什么?

我正在使用 mysql 5.6 和 JDK7。代码如下:

public class ResultSetTest3 

    public static void main(String[] args)
    
        Connection conn;

        try 
            conn = DriverManager.getConnection("jdbc:mysql://localhost/bd", "user", "password");

            Statement sta = conn.createStatement();
            sta.execute("DELETE FROM test");
            sta.close();

            PreparedStatement ps = conn.prepareStatement("INSERT INTO test VALUES(?, ?)");
            for(int i=1; i<=100; i++)
            
                ps.setInt(1, i);
                ps.setString(2, "Teste " + i);
                ps.addBatch();
            
            ps.executeBatch();
            ps.close();

            System.out.println("TYPE_FORWARD_ONLY  CONCUR_READ_ONLY");
            result(conn, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
            System.out.println("===================================");

            System.out.println("TYPE_SCROLL_INSENSITIVE  CONCUR_READ_ONLY");
            result(conn, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
            System.out.println("===================================");

            System.out.println("TYPE_SCROLL_SENSITIVE  CONCUR_READ_ONLY");
            result(conn, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
            System.out.println("===================================");

            System.out.println("TYPE_FORWARD_ONLY  CONCUR_UPDATABLE");
            result(conn, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
            System.out.println("===================================");

            System.out.println("TYPE_SCROLL_INSENSITIVE  CONCUR_UPDATABLE");
            result(conn, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
            System.out.println("===================================");

            System.out.println("TYPE_SCROLL_SENSITIVE  CONCUR_UPDATABLE");
            result(conn, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
            System.out.println("===================================");

            conn.close();
         catch (SQLException e) 
            e.printStackTrace();
        
    

    private static void result(Connection conn, int type, int update) throws SQLException
    
        Statement sta = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
        ResultSet rs = sta.executeQuery("SELECT * FROM test");

        System.out.println(rs.getConcurrency() + " " + update);
        System.out.println(rs.getType() + " " + type);

        try
        
            rs.absolute(10);
            System.out.println(rs.getInt(1) + " - " + rs.getString(2));

            rs.relative(20);
            System.out.println(rs.getInt(1) + " - " + rs.getString(2));

            rs.previous();
            System.out.println(rs.getInt(1) + " - " + rs.getString(2));

            rs.first();
            System.out.println(rs.getInt(1) + " - " + rs.getString(2));

            try 
                System.out.println("AGORA!!!");
                Thread.sleep(20000);
             catch (Exception e) 
                System.out.println(e);
            
            rs.absolute(3);
            System.out.println(rs.getInt(1) + " - " + rs.getString(2));
        
        catch(SQLException e)
        
            System.out.println("Not Scrollable");
        

        try
        
            rs.next();
            rs.next();
            rs.next();
            rs.next();

            rs.deleteRow();

            rs.next();
            rs.updateString(2, "TesteUpdate");

            rs.insertRow();
        
        catch(SQLException e)
        
            System.out.println("Not Updatable");
        

        rs.close();
        sta.close();
    

【问题讨论】:

你的result-方法没有使用typeupdate参数,你总是使用createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY) 另请注意,JDBC 不允许在 FORWARD_ONLY 结果集上滚动,但 MySQL 可能会升级到可滚动的结果集,因为它在默认情况下无论如何都会被缓存。 @MarkRotteveel re:“也许 MySQL 升级到可滚动的结果集” - 情况似乎如此。即使我明确指定ResultSet.TYPE_FORWARD_ONLYrs.last()rs.previous() 在 MySQL ResultSet 上似乎也能正常工作。感谢您提及。 @MarkRotteveel:实际上我更改了调试代码,然后我忘记发布我使用 createStatement(type, update) 的原始代码,但结果是一样的。 @GordThompson:问题在于缓存。使用 fetchSize 方法有效。非常感谢。 【参考方案1】:

尝试用这种方式创建一个Scroll-Insensitive只读ResultSet对象声明对象。

Statement sta = conn.createStatement(
          ResultSet.TYPE_SCROLL_INSENSITIVE,
          ResultSet.CONCUR_READ_ONLY);

而不是

Statement sta = conn.createStatement();

PreparedStatement 也这样做。


通过这个ResultSet 类型,光标可以向任何方向移动。它是不敏感的,这意味着结果集不反映在它仍然打开时所做的更改。它是 MySql 的默认结果集类型。

必读 Retrieving and Modifying Values from Result Sets

【讨论】:

那是用于插入的语句,其中可滚动性无关紧要。问题很简单,他总是在方法result 中创建一个FORWARD_ONLY @MarkRotteveel 可能是 OP 在result() 方法中使用它。【参考方案2】:

正如 Mark Rotteveel 在对该问题的评论中提到的那样,MySQL 默认缓存 ResultSet 数据(也在博客文章中讨论过 本·克里斯滕森here)。这种缓存的一个明显副作用是 MySQL Connector/J 将“升级”一个 TYPE_FORWARD_ONLY ResultSet 以实际可滚动:

Statement s = dbConnection.createStatement(
        ResultSet.TYPE_FORWARD_ONLY, 
        ResultSet.CONCUR_READ_ONLY);
ResultSet rs = s.executeQuery("SELECT * FROM testdata");
rs.last();
System.out.println(String.format("Current row number: %d", rs.getRow()));
rs.previous();
System.out.println(String.format("Current row number: %d", rs.getRow()));

展示

Current row number: 3
Current row number: 2

根据上面引用的博客文章,防止缓存和“流式传输”ResultSet 数据的方法是使用Statement.setFetchSize

Statement s = dbConnection.createStatement(
        ResultSet.TYPE_FORWARD_ONLY, 
        ResultSet.CONCUR_READ_ONLY);
s.setFetchSize(Integer.MIN_VALUE);
ResultSet rs = s.executeQuery("SELECT * FROM testdata");
rs.next();
System.out.println("Data from first row: " + rs.getString(2));
System.out.println("now let's try rs.last() ...");
try 
    rs.last();
    System.out.println("... Okay, done.");
 catch (Exception e) 
    System.out.println("... Exception: " + e.getMessage());

导致

Data from first row: Gord
now let's try rs.last() ...
... Exception: Operation not supported for streaming result sets

【讨论】:

+1 以获得彻底的答案,我没有时间调查 ;)

以上是关于MySQL ResultSet 可滚动/可更新未按预期工作的主要内容,如果未能解决你的问题,请参考以下文章

Mysql Java Derby Netbeans:不允许“deleteRow”,因为 ResultSet 不是可更新的 ResultSet

可滚动的ResultSet类型 实现分页

JDBC进阶 元数据

JDBC API 可滚动可编辑的结果集

Java小技能:分页

防止滚动 AngularJs 上动态更新的可滚动 div