获取玩家统计数据时的MySQLNonTransientConnectionException

时间:2017-07-27 20:45:40

标签: java mysql bukkit

我将我的迷你游戏的玩家统计数据存储在SQL数据库中,并且在加载统计信息时出现以下错误:

com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
[23:37:09] [Server thread/WARN]:    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
[23:37:09] [Server thread/WARN]:    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
[23:37:09] [Server thread/WARN]:    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
[23:37:09] [Server thread/WARN]:    at java.lang.reflect.Constructor.newInstance(Unknown Source)
[23:37:09] [Server thread/WARN]:    at com.mysql.jdbc.Util.handleNewInstance(Util.java:425)
[23:37:09] [Server thread/WARN]:    at com.mysql.jdbc.Util.getInstance(Util.java:408)
[23:37:09] [Server thread/WARN]:    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:918)
[23:37:09] [Server thread/WARN]:    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:897)
[23:37:09] [Server thread/WARN]:    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:886)
[23:37:09] [Server thread/WARN]:    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:860)
[23:37:09] [Server thread/WARN]:    at com.mysql.jdbc.ConnectionImpl.throwConnectionClosedException(ConnectionImpl.java:1187)
[23:37:09] [Server thread/WARN]:    at com.mysql.jdbc.ConnectionImpl.checkClosed(ConnectionImpl.java:1182)
[23:37:09] [Server thread/WARN]:    at com.mysql.jdbc.ConnectionImpl.prepareStatement(ConnectionImpl.java:4035)
[23:37:09] [Server thread/WARN]:    at com.mysql.jdbc.ConnectionImpl.prepareStatement(ConnectionImpl.java:4004)

代码:

public void loadPlayer(Player p) {
    if (!isPlayerInDataBase(p)) {
        Bukkit.getScheduler().runTaskAsynchronously(Main.getInstance(), new Runnable() {
            @Override
            public void run() {
                try (Connection connection = sqlConnection.c) {
                    PreparedStatement insert = connection.prepareStatement(
                            "INSERT INTO `MurderData` (uuid, wins, deaths, loses, kills, score) VALUES (?, ?, ?, ?, ?, ?)");
                    insert.setString(1, p.getUniqueId().toString());
                    insert.setInt(2, 0);
                    insert.setInt(3, 0);
                    insert.setInt(4, 0);
                    insert.setInt(5, 0);
                    insert.setInt(6, 0);
                    insert.execute();
                    ClosePreparedStatement(insert);
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    if (isPlayerInDataBase(p)) {
        Bukkit.getScheduler().runTaskAsynchronously(Main.getInstance(), new Runnable() {
            @Override
            public void run() {
                try (Connection connection = sqlConnection.c;
                     PreparedStatement select = connection.prepareStatement(
                             "SELECT * FROM `MurderData` WHERE uuid='" + p.getUniqueId().toString() + "'")) {
                    ResultSet result = select.executeQuery();
                    if (result.next()) {
                        if (getPlayerData(p) != null) {
                            getPlayerData(p).adddeaths(result.getInt("deaths"));
                            getPlayerData(p).addkill(result.getInt("kills"));
                            getPlayerData(p).addwins(result.getInt("wins"));
                            getPlayerData(p).addlose(result.getInt("loses"));
                            getPlayerData(p).addscore(result.getInt("score"));
                        }
                        CloseResultSet(result);
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

此代码中的错误:

PreparedStatement select = connection.prepareStatement(
                        "SELECT * FROM `MurderData` WHERE uuid='" + p.getUniqueId().toString() + "'")) {

完整代码:

public boolean isPlayerInDataBase(Player p) {
    try (Connection connection = sqlConnection.c;
         PreparedStatement select = connection.prepareStatement(
                 "SELECT * FROM `MurderData` WHERE uuid='" + p.getUniqueId().toString() + "'")) {
        ResultSet result = select.executeQuery();
        if (result.next()) {
            result.close();
            return true;
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return false;
}

附带问题:我应该在完成流程后关闭结果集和准备好的语句吗?我应该立即还是延迟这样做?

2 个答案:

答案 0 :(得分:2)

您似乎已经从其他代码中获取了try-with-resources语句而未理解它的作用。从那个链接:

  

try-with-resources语句是一个声明一个或多个资源的try语句。资源是在程序完成后必须关闭的对象。 try-with-resources语句确保在语句结束时关闭每个资源。实现java.lang.AutoCloseable的任何对象(包括实现java.io.Closeable的所有对象)都可以用作资源。

换句话说,当您使用语法

try (Connection connection = sqlConnection.c) {
     // ...
}

您隐含地在sqlConnection.c上调用了close(),但未再次分配给connection。因此,下次您尝试从此连接查询数据库时,它仍然关闭,因此您会收到此错误。

一个简单的解决方法是将局部变量public boolean isPlayerInDataBase(Player p) { Connection connection = sql.getConnection(); try (PreparedStatement select = connection.prepareStatement( "SELECT * FROM `MurderData` WHERE uuid='" + p.getUniqueId().toString() + "'")) { ResultSet result = select.executeQuery(); if (result.next()) { CloseResultSet(result); return true; } } catch (SQLException e) { e.printStackTrace(); } return false; } 的声明移出try-with-resources语句,这样它就不会在方法结束时关闭。您还应该查看在程序中其他位置使用try-with-resources语法的位置,并确保您没有关闭任何您不想关闭的内容。

这是您的代码的固定版本(来自您在评论中链接的pastebin):

loadPlayer()

此外,在您的if方法中,您可以将两个if (isPlayerInDataBase(p)) { // code if player is in database } else { // code if player is not in database } 语句替换为:

{{1}}

答案 1 :(得分:0)

问题是您的isPlayerInDataBase电话会关闭连接。在发生错误的行中,您正在使用已关闭的相同连接

这就是您收到错误的原因,因为您在连接关闭后尝试执行数据库操作。