使用MySQL数据更新/存储的完美方式,没有延迟和更新统计数据时出错

时间:2017-07-30 23:32:25

标签: mysql bukkit

我在加载/存储MySQL数据时遇到问题,这是我的代码

public void loadPlayer(Player p) {

if (isPlayerInDataBase(p)) {

    Bukkit.getScheduler().runTaskAsynchronously(Main.getInstance(), new Runnable() {
        @Override
        public void run() {
            Connection connection = sql.getConnection();
            try {
                PreparedStatement select = connection
                        .prepareStatement("SELECT * FROM `MurderData` WHERE playername='" + p.getName() + "'");
                ResultSet result = select.executeQuery();

                if (getPlayerData(p) != null) {
                    while (result.next()) {

                        getPlayerData(p).setdeaths(result.getInt("deaths"));
                        getPlayerData(p).setkills(result.getInt("kills"));
                        getPlayerData(p).setwins(result.getInt("wins"));
                        getPlayerData(p).setlose(result.getInt("loses"));
                        getPlayerData(p).setscore(result.getInt("score"));
                        getPlayerData(p).setcoins(result.getInt("coins"));
                    }

                    CloseResultSet(result);

                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

        }
    });
} else {
    Connection connection = sql.getConnection();
    Bukkit.getScheduler().runTaskAsynchronously(Main.getInstance(), new Runnable() {
        @Override
        public void run() {
            try {
                PreparedStatement insert = connection.prepareStatement(
                        "INSERT INTO `MurderData` (playername, wins, deaths, loses, kills, coins, score) VALUES (?, ?, ?, ?, ?, ?, ?)");
                insert.setString(1, p.getName());
                insert.setInt(2, 0);
                insert.setInt(3, 0);
                insert.setInt(4, 0);
                insert.setInt(5, 0);
                insert.setInt(6, 0);
                insert.setInt(7, 0);
                insert.executeUpdate();
                ClosePreparedStatement(insert);
            } catch (SQLException e) {
                e.printStackTrace();
            }

        }

public boolean isPlayerInDataBase(Player p) {
        Connection connection = sql.getConnection();
        try {
            PreparedStatement select = connection
                    .prepareStatement("SELECT * FROM `MurderData` WHERE playername='" + p.getName() + "'");
            ResultSet result = select.executeQuery();
            if (result.next()) {
                CloseResultSet(result);
                return true;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }

所以基本上问题是,我应该在更新MySQL数据后关闭准备好的语句吗?我应该让它有一个像5秒后关闭它的延迟?我可以优化此代码以使其更好吗?

数据库中的播放器是否正常?

这是我的两个关闭方法:

public void CloseResultSet(ResultSet s) {

        new BukkitRunnable() {

            @Override
            public void run() {

                if (s != null) {
                    try {
                        s.close();
                    } catch (SQLException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }

            }
        }.runTaskLater(this, 20 * getConfig().getInt("close-sql-statements-after"));
    }

    public void ClosePreparedStatement(PreparedStatement s) {

        new BukkitRunnable() {

            @Override
            public void run() {

                if (s != null) {
                    try {
                        s.close();
                    } catch (SQLException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }

            }
        }.runTaskLater(this, 20 * getConfig().getInt("close-sql-statements-after"));

    }

更新:这是第二个问题,第一个问题是小延迟峰值第二个问题是在第252行存储数据

public void setSQLData(Player p, int kills, int deaths, int loses, int wins, int coins, int score) {
        Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
            @Override
            public void run() {
                Connection connection = plugin.sql.getConnection();
                try {
                    PreparedStatement insert = connection.prepareStatement(
                            "INSERT INTO `MurderData` (playername, wins, deaths, loses, kills, coins, score) VALUES (?, ?, ?, ?, ?, ?, ?)");
                    insert.setString(1, p.getName());
                    insert.setInt(2, wins);
                    insert.setInt(3, deaths);
                    insert.setInt(4, loses);
                    insert.setInt(5, kills);
                    insert.setInt(6, coins);
                    insert.setInt(7, score);
                    insert.executeUpdate();// error line 252
                    plugin.ClosePreparedStatement(insert);
                } catch (SQLException e) {
                    e.printStackTrace();
                }

            }

        });
    }

     at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at java.lang.reflect.Constructor.newInstance(Unknown Source)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at com.mysql.jdbc.Util.handleNewInstance(Util.java:407)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1116)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3082)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2968)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3516)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1986)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2140)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2626)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2111)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2407)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2325)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2310)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at me.joseph.murder.api.MurderAPI$1.run(MurderAPI.java:252)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at org.bukkit.craftbukkit.v1_8_R3.scheduler.CraftTask.run(CraftTask.java:71)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at org.bukkit.craftbukkit.v1_8_R3.scheduler.CraftAsyncTask.run(CraftAsyncTask.java:53)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at java.lang.Thread.run(Unknown Source)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:2529)
[22:31:18] [Craft Scheduler Thread - 71/WARN]:  at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2979)

2 个答案:

答案 0 :(得分:1)

首先:

据我所见,每次执行Query / Update后都会关闭连接。不幸的是,这非常不合适。如果在插件启动时打开MySQL连接并在插件停止时再次关闭它会更好。

我个人这样做:(并不意味着你必须这样做)

连接变量:

private static Connection connection;

连接功能:

    public static void connect(String host, String user, String password, String database) {
    Bukkit.getScheduler().runTaskAsynchronously(Main.getInstance(), () -> {

        //close Connection if still active
        if (connection != null) {
            close();
        }

        //connect to database host
        try {
            Class.forName("com.mysql.jdbc.Driver");

            connection = DriverManager.getConnection("jdbc:mysql://" + host + "/" + database, user, password);

        } catch (SQLException e) {
          System.out.println(e.getMessage());
        }

    });
}

我将数据更新/写入数据库的功能:

    public void Update(final String qry) {
    Bukkit.getScheduler().runTaskAsynchronously(Main.getInstance(), () -> {
        try {
            Statement stnt = connection.createStatement();
            stnt.executeUpdate(qry);

        } catch (SQLException e) {
            e.printStackTrace();
        }
    });

}

我从数据库查询信息的功能:

正如您所看到的,此功能不是异步的。遗憾的是,我现在没有管理它以使这个函数异步。但是你可以通过使函数调用异步来轻松解决这个问题。例如: Bukkit.getScheduler().runTaskAsynchronously(Main.getInstance(), () -> { Query("your query"); });

    public ResultSet Query(String qry) {
    ResultSet rs = null;

    try {
        Statement stnt = connection.createStatement();
        rs = stnt.executeQuery(qry);
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return rs;
}

关闭功能:

    public static void close() {
    Bukkit.getScheduler().runTaskAsynchronously(Main.getInstance(), () -> {
        try {
            connection.close();
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        }
    });
}

这种方式的缺点是你一次只能连接一个数据库(在我的情况下就好了)。

希望它对你有所帮助。我也因为奇怪的MySQL错误而苦苦挣扎。幸运的是,使用此代码一切正常。

回答你的问题:

[22:31:18] [Craft Scheduler Thread - 71/WARN]: Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.

据我所知,这意味着即使插件尝试使用它,也会关闭与MySQL服务器的连接。 如上所述,在开始时打开一个连接并保持打开直到插件停止应该解决这个问题。

答案 1 :(得分:0)

感谢所有帮助我解决问题的人

 public void setSQLData(Player p, int kills, int deaths, int loses, int wins, int coins, int score) {


  Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
        @Override
        public void run() {
            Connection connection = plugin.sql.getConnection();
            try {
                PreparedStatement insert = connection.prepareStatement(
                        "INSERT INTO `MurderData` (playername, wins, deaths, loses, kills, coins, score) VALUES (?, ?, ?, ?, ?, ?, ?)");

应该是UPDATE而不是插入