SQL 结果集突然关闭

SQL ResultSet closing suddently

您好 Whosebug 用户,

目前我的 SQL 连接出现问题。首先,这里是连接器的处理方式

package com.rs.utils.sql;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

import com.rs.utils.Logger;

public class DatabaseManager {

private String host;
private String database;
private String username;
private String password;

private Connection connection;
private PreparedStatement statement;

private boolean connected;

public DatabaseManager() {
this.host = "";// Settings.DB_HOST;
this.database = "";// Settings.DB_NAME;
this.username = "";// Settings.DB_USER;
this.password = "";// Settings.DB_PASS;
this.connected = false;
}

public void connect() {
    try {
        connection = DriverManager.getConnection("jdbc:mysql://" + host
                + "/" + database
                + "?jdbcCompliantTruncation=false&autoReconnect=true",
                username, password);
        Logger.info("Successfully connected with " + host + "/" + database);
        connected = true;
    } catch (Exception e) {
        Logger.info("Unable to connect with " + host + "/" + database + ".");
        connected = false;
    }
}

public ResultSet executeQuery(String query) {
    try {

        if (!connected())
            return null;

        statement = connection.prepareStatement(query);
        ResultSet results = statement.executeQuery();
        return results;
    } catch (Exception e) {
        Logger.handle(e);
    }
    return null;
}

public int executeUpdate(String query) {
    try {

        if (!connected())
            return 0;

        statement = connection.prepareStatement(query);
        return statement.executeUpdate();
    } catch (Exception e) {
        Logger.handle(e);
    }

    return 0;
}

public boolean connected() {
    return connected;
}

public PreparedStatement statement() {
    return statement;
}

}

使用 DatabaseManager 实例向数据库发送大量查询,例如:

databaseManager.executeUpdate(query);    

databaseManager 定义为:

private static DatabaseManager databaseManager = new DatabaseManager();    

我得到以下 Throwable:

java.sql.SQLException: Operation not allowed after ResultSet closed
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1074)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:988)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:974)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:919)
at com.mysql.jdbc.ResultSetImpl.checkClosed(ResultSetImpl.java:803)
at com.mysql.jdbc.ResultSetImpl.findColumn(ResultSetImpl.java:1126)
at com.mysql.jdbc.ResultSetImpl.getString(ResultSetImpl.java:5732)
at com.rs.utils.sql.PlayerSaving.load(PlayerSaving.java:362)
at com.rs.utils.sql.PlayerLoader.load(PlayerLoader.java:90)    

PlayerLoader 中的加载方法:

public static boolean load(Player player, boolean lobby) {
    ResultSet result = null;
    try {
        final long current = System.currentTimeMillis();
        result = World.database().executeQuery("SELECT " + (lobby ? "displayName, rights" : "*") + " FROM " + PLAYER_TABLE + " WHERE username='" + player.getPlayerDefinition().username() + "' LIMIT 1");

        if (!result.next()) {
            return false;
        }

        if (lobby) {
            player.getPlayerDefinition().setRights(result.getInt("rights")).setDisplayName(result.getString("displayName"));
        } else {
            **player.playerSaving().load(result);**
        }

        Logger.log("Loader", "Player loaded in " + (System.currentTimeMillis() - current) + "ms.");
        return true;
    } catch (Exception e) {
        Logger.log("Loader", "Unable to load player profile.");
        Logger.handle(e);
        System.err.println("Error Loading the account.");
    } finally {
        try {
            if (result != null) {
                result.close();
            }
            result = null;
            World.database().statement().close();
        } catch (SQLException e) {
            Logger.handle(e);
        }
    }
    return false;
}    

PlayerSaving 加载方法只是将数据库中的值分配给另一个 class 实例 result.getInt("rowName") 等,在 ResultSet 上收到,它超过 250 行

ResultSet 大多数时候都很好,这是非常罕见的事件,例如 100 次登录尝试中只有 1 次。但是不知何故 ResultSet 在变量赋值的中间关闭了,比如,它分配了 200 个变量中的 100 个,然后 ResultSet 突然关闭并抛出异常。

有什么想法吗?

关闭语句也将关闭底层 ResultSet。您可能对只有一个共享 PreparedStatement 的 DatabaseManager 进行了嵌套调用,在我看来,这是个坏主意。

(我没有足够的代表来添加评论,所以我不得不 post 这个作为答案)

下面是为每个查询操作使用专用 PreparedStatement 和 ResultSet 的示例实现:

public List<MyDataClass> listMyData(Connection con, String partNumber) {
    ArrayList<MyDataClass> list = new ArrayList<MyDataClass>();
    PreparedStatement ps = null;
    ResultSet rs = null;

    try {
        ps = con.prepareStatement("SELECT PART_NO, DESC, PRICE FROM MYTABLE WHERE PART_NO = ?");
        ps.setString(1, partNumber);
        rs = ps.executeQuery();
        while(rs.next()) {
            MyDataClass myData = new MyDataClass();
            myData.setPartNumber(rs.getString("PART_NO"));
            myData.setDescription(rs.getString("DESC"));
            myData.setPrice(rs.getBigDecimal("PRICE"));
            list.add(myData);
        }
        con.commit();
    }
    catch(Throwable thrown) {
        try{ con.rollback(); }catch(Throwable t){}
        //Handle the exception here to log etc.
    }
    finally {
        //Always close result set before statement
        if(rs != null) { try{ rs.close(); }catch(Throwable t){} }
        if(ps != null) { try{ ps.close(); }catch(Throwable t){} }
    }

    return list;
}